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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/build/build-cjs/index.js +2228 -1420
  2. package/build/build-cjs/src/booking-wizard/features/booking/selectors.d.ts +1 -1
  3. package/build/build-cjs/src/booking-wizard/features/travelers-form/validate-form.d.ts +2 -2
  4. package/build/build-cjs/src/booking-wizard/types.d.ts +23 -2
  5. package/build/build-cjs/src/search-results/components/flight/flight-selection/independent-flight-option.d.ts +2 -0
  6. package/build/build-cjs/src/search-results/components/itinerary/index.d.ts +1 -0
  7. package/build/build-cjs/src/search-results/components/itinerary/itinerary-utils.d.ts +1 -0
  8. package/build/build-cjs/src/search-results/types.d.ts +8 -0
  9. package/build/build-cjs/src/search-results/utils/booked-entry-utils.d.ts +20 -0
  10. package/build/build-cjs/src/search-results/utils/traject-utils.d.ts +7 -0
  11. package/build/build-cjs/src/shared/booking/travelers-form.d.ts +13 -8
  12. package/build/build-esm/index.js +2232 -1424
  13. package/build/build-esm/src/booking-wizard/features/booking/selectors.d.ts +1 -1
  14. package/build/build-esm/src/booking-wizard/features/travelers-form/validate-form.d.ts +2 -2
  15. package/build/build-esm/src/booking-wizard/types.d.ts +23 -2
  16. package/build/build-esm/src/search-results/components/flight/flight-selection/independent-flight-option.d.ts +2 -0
  17. package/build/build-esm/src/search-results/components/itinerary/index.d.ts +1 -0
  18. package/build/build-esm/src/search-results/components/itinerary/itinerary-utils.d.ts +1 -0
  19. package/build/build-esm/src/search-results/types.d.ts +8 -0
  20. package/build/build-esm/src/search-results/utils/booked-entry-utils.d.ts +20 -0
  21. package/build/build-esm/src/search-results/utils/traject-utils.d.ts +7 -0
  22. package/build/build-esm/src/shared/booking/travelers-form.d.ts +13 -8
  23. package/package.json +2 -2
  24. package/styles/components/_slider.scss +0 -8
@@ -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, useCallback, useMemo } from 'react';
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, parseISO, differenceInMinutes, parse, isValid, 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, isPlainObject as isPlainObject$1, mapValues, pickBy, first, orderBy, uniq, uniqBy, sortBy, sum, last, findIndex as findIndex$1, get, set, isEqual as isEqual$1, groupBy, minBy, concat, flatMap } from 'lodash';
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,21 @@ 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",
185
202
  WEIGHT: "الوزن",
186
203
  WEIGHT_UNIT_KG: "kg",
187
204
  WEIGHT_UNIT_LB: "lb",
@@ -577,7 +594,9 @@ var SHARED$e = {
577
594
  ADULTS: "Voksne",
578
595
  CHILDREN: "Børn",
579
596
  SELECT: "Vælg",
580
- SELECTED: "Valgt"
597
+ SELECTED: "Valgt",
598
+ BOOKED: "Booket",
599
+ SAVE: "Gem"
581
600
  };
582
601
  var SIDEBAR$e = {
583
602
  OVERVIEW: "Oversigt",
@@ -635,6 +654,21 @@ var TRAVELERS_FORM$e = {
635
654
  FIRST_NAME: "Fornavn",
636
655
  LAST_NAME: "Efternavn",
637
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",
638
672
  WEIGHT: "Vægt",
639
673
  WEIGHT_UNIT_KG: "kg",
640
674
  WEIGHT_UNIT_LB: "lb",
@@ -1030,7 +1064,9 @@ var SHARED$d = {
1030
1064
  ADULTS: "Erwachsene",
1031
1065
  CHILDREN: "Kinder",
1032
1066
  SELECT: "Auswählen",
1033
- SELECTED: "Ausgewählt"
1067
+ SELECTED: "Ausgewählt",
1068
+ BOOKED: "Gebucht",
1069
+ SAVE: "Speichern"
1034
1070
  };
1035
1071
  var SIDEBAR$d = {
1036
1072
  OVERVIEW: "Übersicht",
@@ -1088,6 +1124,21 @@ var TRAVELERS_FORM$d = {
1088
1124
  FIRST_NAME: "Vorname",
1089
1125
  LAST_NAME: "Nachname",
1090
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",
1091
1142
  WEIGHT: "Gewicht",
1092
1143
  WEIGHT_UNIT_KG: "kg",
1093
1144
  WEIGHT_UNIT_LB: "lb",
@@ -1483,7 +1534,9 @@ var SHARED$c = {
1483
1534
  ADULTS: "Adults",
1484
1535
  CHILDREN: "Children",
1485
1536
  SELECT: "Select",
1486
- SELECTED: "Selected"
1537
+ SELECTED: "Selected",
1538
+ BOOKED: "Booked",
1539
+ SAVE: "Save"
1487
1540
  };
1488
1541
  var SIDEBAR$c = {
1489
1542
  OVERVIEW: "Summary",
@@ -1542,6 +1595,21 @@ var TRAVELERS_FORM$c = {
1542
1595
  FIRST_NAME: "First name",
1543
1596
  LAST_NAME: "Last name",
1544
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",
1545
1613
  WEIGHT: "Weight",
1546
1614
  WEIGHT_UNIT_KG: "kg",
1547
1615
  WEIGHT_UNIT_LB: "lb",
@@ -1940,7 +2008,9 @@ var SHARED$b = {
1940
2008
  ADULTS: "Adultos",
1941
2009
  CHILDREN: "Niños",
1942
2010
  SELECT: "Seleccionar",
1943
- SELECTED: "Seleccionado"
2011
+ SELECTED: "Seleccionado",
2012
+ BOOKED: "Reservado",
2013
+ SAVE: "Guardar"
1944
2014
  };
1945
2015
  var SIDEBAR$b = {
1946
2016
  OVERVIEW: "Resumen",
@@ -1998,6 +2068,21 @@ var TRAVELERS_FORM$b = {
1998
2068
  FIRST_NAME: "Nombre",
1999
2069
  LAST_NAME: "Apellido",
2000
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",
2001
2086
  WEIGHT: "Peso",
2002
2087
  WEIGHT_UNIT_KG: "kg",
2003
2088
  WEIGHT_UNIT_LB: "lb",
@@ -2393,7 +2478,9 @@ var SHARED$a = {
2393
2478
  ADULTS: "Adultes",
2394
2479
  CHILDREN: "Enfants",
2395
2480
  SELECT: "Sélectionner",
2396
- SELECTED: "Sélectionné"
2481
+ SELECTED: "Sélectionné",
2482
+ BOOKED: "Réservé",
2483
+ SAVE: "Enregistrer"
2397
2484
  };
2398
2485
  var SIDEBAR$a = {
2399
2486
  OVERVIEW: "Aperçu",
@@ -2452,6 +2539,21 @@ var TRAVELERS_FORM$a = {
2452
2539
  FIRST_NAME: "Prénom",
2453
2540
  LAST_NAME: "Nom",
2454
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",
2455
2557
  WEIGHT: "Poids",
2456
2558
  WEIGHT_UNIT_KG: "kg",
2457
2559
  WEIGHT_UNIT_LB: "lb",
@@ -2850,7 +2952,9 @@ var SHARED$9 = {
2850
2952
  ADULTS: "Adultes",
2851
2953
  CHILDREN: "Enfants",
2852
2954
  SELECT: "Sélectionner",
2853
- SELECTED: "Sélectionné"
2955
+ SELECTED: "Sélectionné",
2956
+ BOOKED: "Réservé",
2957
+ SAVE: "Enregistrer"
2854
2958
  };
2855
2959
  var SIDEBAR$9 = {
2856
2960
  OVERVIEW: "Résumé",
@@ -2908,6 +3012,21 @@ var TRAVELERS_FORM$9 = {
2908
3012
  FIRST_NAME: "Prénom",
2909
3013
  LAST_NAME: "Nom",
2910
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",
2911
3030
  WEIGHT: "Poids",
2912
3031
  WEIGHT_UNIT_KG: "kg",
2913
3032
  WEIGHT_UNIT_LB: "lb",
@@ -3303,7 +3422,9 @@ var SHARED$8 = {
3303
3422
  ADULTS: "Fullorðnir",
3304
3423
  CHILDREN: "Börn",
3305
3424
  SELECT: "Velja",
3306
- SELECTED: "Valið"
3425
+ SELECTED: "Valið",
3426
+ BOOKED: "Bókað",
3427
+ SAVE: "Vista"
3307
3428
  };
3308
3429
  var SIDEBAR$8 = {
3309
3430
  OVERVIEW: "Yfirlit",
@@ -3361,6 +3482,21 @@ var TRAVELERS_FORM$8 = {
3361
3482
  FIRST_NAME: "Fornafn",
3362
3483
  LAST_NAME: "Eftirnafn",
3363
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",
3364
3500
  WEIGHT: "Þyngd",
3365
3501
  WEIGHT_UNIT_KG: "kg",
3366
3502
  WEIGHT_UNIT_LB: "lb",
@@ -3756,7 +3892,9 @@ var SHARED$7 = {
3756
3892
  ADULTS: "Adulti",
3757
3893
  CHILDREN: "Bambini",
3758
3894
  SELECT: "Seleziona",
3759
- SELECTED: "Selezionato"
3895
+ SELECTED: "Selezionato",
3896
+ BOOKED: "Prenotato",
3897
+ SAVE: "Salva"
3760
3898
  };
3761
3899
  var SIDEBAR$7 = {
3762
3900
  OVERVIEW: "Riepilogo",
@@ -3814,6 +3952,21 @@ var TRAVELERS_FORM$7 = {
3814
3952
  FIRST_NAME: "Nome",
3815
3953
  LAST_NAME: "Cognome",
3816
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",
3817
3970
  WEIGHT: "Peso",
3818
3971
  WEIGHT_UNIT_KG: "kg",
3819
3972
  WEIGHT_UNIT_LB: "lb",
@@ -4209,7 +4362,9 @@ var SHARED$6 = {
4209
4362
  ADULTS: "Volwassenen",
4210
4363
  CHILDREN: "Kinderen",
4211
4364
  SELECT: "Selecteer",
4212
- SELECTED: "Geselecteerd"
4365
+ SELECTED: "Geselecteerd",
4366
+ BOOKED: "Geboekt",
4367
+ SAVE: "Opslaan"
4213
4368
  };
4214
4369
  var SIDEBAR$6 = {
4215
4370
  OVERVIEW: "Overzicht",
@@ -4268,6 +4423,21 @@ var TRAVELERS_FORM$6 = {
4268
4423
  FIRST_NAME: "Voornaam",
4269
4424
  LAST_NAME: "Naam",
4270
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",
4271
4441
  WEIGHT: "Gewicht",
4272
4442
  WEIGHT_UNIT_KG: "kg",
4273
4443
  WEIGHT_UNIT_LB: "lb",
@@ -4666,7 +4836,9 @@ var SHARED$5 = {
4666
4836
  ADULTS: "Volwassenen",
4667
4837
  CHILDREN: "Kinderen",
4668
4838
  SELECT: "Selecteren",
4669
- SELECTED: "Geselecteerd"
4839
+ SELECTED: "Geselecteerd",
4840
+ BOOKED: "Geboekt",
4841
+ SAVE: "Opslaan"
4670
4842
  };
4671
4843
  var SIDEBAR$5 = {
4672
4844
  OVERVIEW: "Overzicht",
@@ -4724,6 +4896,21 @@ var TRAVELERS_FORM$5 = {
4724
4896
  FIRST_NAME: "Voornaam",
4725
4897
  LAST_NAME: "Achternaam",
4726
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",
4727
4914
  WEIGHT: "Gewicht",
4728
4915
  WEIGHT_UNIT_KG: "kg",
4729
4916
  WEIGHT_UNIT_LB: "lb",
@@ -5119,7 +5306,9 @@ var SHARED$4 = {
5119
5306
  ADULTS: "Voksne",
5120
5307
  CHILDREN: "Barn",
5121
5308
  SELECT: "Velg",
5122
- SELECTED: "Valgt"
5309
+ SELECTED: "Valgt",
5310
+ BOOKED: "Booket",
5311
+ SAVE: "Lagre"
5123
5312
  };
5124
5313
  var SIDEBAR$4 = {
5125
5314
  OVERVIEW: "Oversikt",
@@ -5177,6 +5366,21 @@ var TRAVELERS_FORM$4 = {
5177
5366
  FIRST_NAME: "Fornavn",
5178
5367
  LAST_NAME: "Etternavn",
5179
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",
5180
5384
  WEIGHT: "Vekt",
5181
5385
  WEIGHT_UNIT_KG: "kg",
5182
5386
  WEIGHT_UNIT_LB: "lb",
@@ -5572,7 +5776,9 @@ var SHARED$3 = {
5572
5776
  ADULTS: "Dorośli",
5573
5777
  CHILDREN: "Dzieci",
5574
5778
  SELECT: "Wybierz",
5575
- SELECTED: "Wybrane"
5779
+ SELECTED: "Wybrane",
5780
+ BOOKED: "Zarezerwowane",
5781
+ SAVE: "Zapisz"
5576
5782
  };
5577
5783
  var SIDEBAR$3 = {
5578
5784
  OVERVIEW: "Podsumowanie",
@@ -5630,6 +5836,21 @@ var TRAVELERS_FORM$3 = {
5630
5836
  FIRST_NAME: "Imię",
5631
5837
  LAST_NAME: "Nazwisko",
5632
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",
5633
5854
  WEIGHT: "Waga",
5634
5855
  WEIGHT_UNIT_KG: "kg",
5635
5856
  WEIGHT_UNIT_LB: "lb",
@@ -6025,7 +6246,9 @@ var SHARED$2 = {
6025
6246
  ADULTS: "Adultos",
6026
6247
  CHILDREN: "Crianças",
6027
6248
  SELECT: "Selecionar",
6028
- SELECTED: "Selecionado"
6249
+ SELECTED: "Selecionado",
6250
+ BOOKED: "Reservado",
6251
+ SAVE: "Guardar"
6029
6252
  };
6030
6253
  var SIDEBAR$2 = {
6031
6254
  OVERVIEW: "Resumo",
@@ -6083,6 +6306,21 @@ var TRAVELERS_FORM$2 = {
6083
6306
  FIRST_NAME: "Nome",
6084
6307
  LAST_NAME: "Apelido",
6085
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",
6086
6324
  WEIGHT: "Peso",
6087
6325
  WEIGHT_UNIT_KG: "kg",
6088
6326
  WEIGHT_UNIT_LB: "lb",
@@ -6478,7 +6716,9 @@ var SHARED$1 = {
6478
6716
  ADULTS: "Vuxna",
6479
6717
  CHILDREN: "Barn",
6480
6718
  SELECT: "Välj",
6481
- SELECTED: "Vald"
6719
+ SELECTED: "Vald",
6720
+ BOOKED: "Bokad",
6721
+ SAVE: "Spara"
6482
6722
  };
6483
6723
  var SIDEBAR$1 = {
6484
6724
  OVERVIEW: "Översikt",
@@ -6536,6 +6776,21 @@ var TRAVELERS_FORM$1 = {
6536
6776
  FIRST_NAME: "Förnamn",
6537
6777
  LAST_NAME: "Efternamn",
6538
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",
6539
6794
  WEIGHT: "Vikt",
6540
6795
  WEIGHT_UNIT_KG: "kg",
6541
6796
  WEIGHT_UNIT_LB: "lb",
@@ -6931,7 +7186,9 @@ var SHARED = {
6931
7186
  ADULTS: "大人",
6932
7187
  CHILDREN: "子供",
6933
7188
  SELECT: "選択",
6934
- SELECTED: "選択済み"
7189
+ SELECTED: "選択済み",
7190
+ BOOKED: "予約済み",
7191
+ SAVE: "保存"
6935
7192
  };
6936
7193
  var SIDEBAR = {
6937
7194
  OVERVIEW: "概要",
@@ -6989,6 +7246,21 @@ var TRAVELERS_FORM = {
6989
7246
  FIRST_NAME: "名",
6990
7247
  LAST_NAME: "姓",
6991
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",
6992
7264
  WEIGHT: "体重",
6993
7265
  WEIGHT_UNIT_KG: "kg",
6994
7266
  WEIGHT_UNIT_LB: "lb",
@@ -15092,6 +15364,34 @@ var getContentDraft = function (config, signal) {
15092
15364
  var apiKey = config.apiKey;
15093
15365
  return get(url, apiKey, config.token, signal, true);
15094
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
+ };
15095
15395
  /**
15096
15396
  * api/web/content/live/by-property
15097
15397
  * Gets Live content matching a property value (or values).
@@ -15219,6 +15519,7 @@ var ENDPOINT_TRAJECT = ENDPOINT$1 + "/traject";
15219
15519
  var ENDPOINT_ENTRY = function (magicLinkCode) {
15220
15520
  return ENDPOINT$1 + "/entry/" + magicLinkCode;
15221
15521
  };
15522
+ var ENDPOINT_SAVE_ENTRY = ENDPOINT$1 + "/entry/save";
15222
15523
  var startTransaction = function (config, signal) {
15223
15524
  var apiKey = config.apiKey;
15224
15525
  var url = "" + config.host + ENDPOINT_START_TRANSACTION;
@@ -15247,6 +15548,12 @@ var getEntry = function (config, magicLinkCode, signal) {
15247
15548
  var url = "" + config.host + ENDPOINT_ENTRY(magicLinkCode);
15248
15549
  return get(url, apiKey, config.token, signal, true);
15249
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
+ };
15250
15557
  var getTrajectEntry = function (config, request, signal) {
15251
15558
  var url = "" + config.host + ENDPOINT_TRAJECT;
15252
15559
  var apiKey = config.apiKey;
@@ -15381,6 +15688,8 @@ exports.getEntryList = getEntryList;
15381
15688
  exports.getEntryListv2 = getEntryListv2;
15382
15689
  exports.getEntryStatus = getEntryStatus;
15383
15690
  exports.getEntryTotals = getEntryTotals;
15691
+ exports.getFilteredContentDraft = getFilteredContentDraft;
15692
+ exports.getFilteredContentLive = getFilteredContentLive;
15384
15693
  exports.getInvoiceList = getInvoiceList;
15385
15694
  exports.getItinerary = getItinerary;
15386
15695
  exports.getLocations = getLocations;
@@ -15401,6 +15710,7 @@ exports.print = print;
15401
15710
  exports.readForm = readForm;
15402
15711
  exports.repriceAndIssueTicket = repriceAndIssueTicket;
15403
15712
  exports.resetPassword = resetPassword;
15713
+ exports.savePackagingEntry = savePackagingEntry;
15404
15714
  exports.search = search;
15405
15715
  exports.searchCountries = searchCountries;
15406
15716
  exports.searchElastic = searchElastic;
@@ -18731,335 +19041,1163 @@ const SUMMARY_STEP = 5;
18731
19041
  const CONFIRMATION_STEP = 6;
18732
19042
  const ERROR_STEP = 7;
18733
19043
 
18734
- const fetchPriceDetails$1 = async (apiSettings, request, signal, languageCode) => {
18735
- const tideClientConfig = buildTideClientConfig(apiSettings);
18736
- return await build.priceDetails(tideClientConfig, request, signal, languageCode);
18737
- };
18738
- const priceDetailsApi = {
18739
- 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 })));
18740
19048
  };
18741
19049
 
18742
- const updatePackageDetails = (packageDetails, changedLines) => {
18743
- return {
18744
- ...packageDetails,
18745
- options: packageDetails.options.map((opt) => {
18746
- return {
18747
- ...opt,
18748
- rooms: opt.isSelected ? updateRooms(opt.rooms, changedLines) : opt.rooms,
18749
- groups: opt.isSelected ? updateBookingGroups(opt.groups, changedLines) : opt.groups,
18750
- optionUnits: opt.isSelected ? updateOptionUnits(opt.optionUnits, changedLines) : opt.optionUnits,
18751
- optionPax: opt.isSelected ? updateOptionPax(opt.optionPax, changedLines) : opt.optionPax
18752
- };
18753
- })
18754
- };
18755
- };
18756
- // ROOMS
18757
- const updateRooms = (rooms, changedLines) => {
18758
- return rooms.map((room) => ({
18759
- ...room,
18760
- options: room.options.map((option) => ({
18761
- ...option,
18762
- price: changedLines.find((x) => x.entryLineGuid == option.entryLineGuid)?.price ?? option.price
18763
- }))
18764
- }));
18765
- };
18766
- // PAX
18767
- const updateOptionPax = (pax, changedLines) => {
18768
- return pax.map((p) => ({
18769
- ...p,
18770
- groups: updatePaxGroups(p.groups, changedLines)
18771
- }));
18772
- };
18773
- const updatePaxGroups = (groups, changedLines) => {
18774
- return groups.map((g) => ({
18775
- ...g,
18776
- options: g.options.map((o) => ({
18777
- ...o,
18778
- groups: updatePaxGroups(o.groups, changedLines),
18779
- line: {
18780
- ...o.line,
18781
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18782
- },
18783
- alternatives: o.alternatives.map((a) => ({
18784
- ...a,
18785
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18786
- }))
18787
- }))
18788
- }));
18789
- };
18790
- // UNITS
18791
- const updateOptionUnits = (units, changedLines) => {
18792
- return units.map((u) => ({
18793
- ...u,
18794
- groups: updateUnitGroups(u.groups, changedLines)
18795
- }));
18796
- };
18797
- const updateUnitGroups = (groups, changedLines) => {
18798
- return groups.map((g) => ({
18799
- ...g,
18800
- options: g.options.map((o) => ({
18801
- ...o,
18802
- groups: updateUnitGroups(o.groups, changedLines),
18803
- pax: updateOptionPax(o.pax, changedLines),
18804
- line: {
18805
- ...o.line,
18806
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18807
- },
18808
- alternatives: o.alternatives.map((a) => ({
18809
- ...a,
18810
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18811
- }))
18812
- }))
18813
- }));
18814
- };
18815
- // BOOKING
18816
- const updateBookingGroups = (groups, changedLines) => {
18817
- return groups.map((g) => ({
18818
- ...g,
18819
- options: g.options.map((o) => ({
18820
- ...o,
18821
- groups: updateBookingGroups(o.groups, changedLines),
18822
- units: updateOptionUnits(o.units, changedLines),
18823
- pax: updateOptionPax(o.pax, changedLines),
18824
- line: {
18825
- ...o.line,
18826
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18827
- },
18828
- alternatives: o.alternatives.map((a) => ({
18829
- ...a,
18830
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18831
- }))
18832
- }))
18833
- }));
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)))))));
18834
19055
  };
18835
19056
 
18836
- const fetchPriceDetails = createAsyncThunk('priceDetails/fetchPriceDetails', async (_, { dispatch, getState, signal }) => {
18837
- const state = getState();
18838
- const languageCode = selectLanguageCode(state);
18839
- const request = selectBookingPackageBookRequest(state);
18840
- if (!request) {
18841
- throw Error('Product not available.');
18842
- }
18843
- dispatch(setFetchingPriceDetails(true));
18844
- dispatch(resetPriceDetails());
18845
- const priceDetails = await priceDetailsApi.fetchPriceDetails(state.apiSettings, request, signal, languageCode);
18846
- dispatch(setFetchingPriceDetails(false));
18847
- const packageDetails = selectPackageDetails(state);
18848
- if (packageDetails) {
18849
- const updatedPackageDetails = updatePackageDetails(packageDetails, priceDetails.changedLines);
18850
- 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 };
18851
19071
  }
18852
- return priceDetails;
18853
- });
18854
- const priceDetailsSlice = createSlice({
18855
- name: 'priceDetails',
18856
- initialState: {
18857
- priceDetails: [],
18858
- notifications: [],
18859
- isBusy: false
18860
- },
18861
- reducers: {
18862
- setFetchingPriceDetails(state, action) {
18863
- state.isBusy = action.payload;
18864
- },
18865
- setNotifications(state, action) {
18866
- state.notifications = action.payload;
18867
- },
18868
- resetPriceDetails(state, _) {
18869
- state.priceDetails = [];
18870
- }
18871
- },
18872
- extraReducers: (builder) => {
18873
- builder.addCase(fetchPriceDetails.fulfilled, (state, action) => {
18874
- if (action.payload) {
18875
- state.deposit = action.payload.deposit;
18876
- state.commission = action.payload.commission;
18877
- state.priceDetails = action.payload.details;
18878
- state.notifications = action.payload.notifications;
18879
- }
18880
- });
18881
- builder.addCase(fetchPriceDetails.rejected, (state) => {
18882
- 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 }
18883
19100
  });
18884
- }
18885
- });
18886
- const { setFetchingPriceDetails } = priceDetailsSlice.actions;
18887
- const { resetPriceDetails, setNotifications } = priceDetailsSlice.actions;
18888
- const selectNotifications = (state) => state.priceDetails.notifications;
18889
- var priceDetailsReducer = priceDetailsSlice.reducer;
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
+ };
18890
19134
 
18891
- const selectHasMounted = (state) => state.booking.hasMounted;
18892
- const selectGeneratePaymentUrl = (state) => state.booking.generatePaymentUrl;
18893
- const selectSkipPaymentWithAgent = (state) => state.booking.skipPaymentWithAgent;
18894
- const selectIsFetchingProductOptions = (state) => state.booking.isBusy;
18895
- const selectDepartureFlight = (state) => state.booking.package?.outwardFlights?.find((x) => x.isSelected);
18896
- const selectReturnFlight = (state) => state.booking.package?.returnFlights?.find((x) => x.isSelected);
18897
- const selectPackageRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.rooms;
18898
- const selectIncludedServiceTypes = (state) => selectActiveOption(state)?.includedServiceTypes;
18899
- const selectAvailabilities = (state) => state.booking.package?.options.find((x) => x.isSelected)?.availabilities;
18900
- const selectPackageTags = (state) => state.booking.package?.tags;
18901
- const selectIsOnRequest = (state) => state.booking.package?.options.find((x) => x.isSelected)?.isOnRequest;
18902
- const selectPackageOptionUnits = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionUnits;
18903
- const selectPackageOptionPax = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionPax;
18904
- const selectPackageGroups = (state) => state.booking.package?.options.find((x) => x.isSelected)?.groups;
18905
- const selectPackageDetails = (state) => state.booking.package;
18906
- const selectPaxCustomFields = (state) => state.booking.package?.paxCustomFields ?? [];
18907
- const selectPackageFlights = (state) => ({
18908
- outward: state.booking.package?.outwardFlights,
18909
- return: state.booking.package?.returnFlights
18910
- });
18911
- const selectFlightMetaData = (state) => state.booking.package?.flightInfo.metaData;
18912
- const selectActiveOption = (state) => state.booking.package?.options.find((x) => x.isSelected);
18913
- const selectPackageAirlineGroups = createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
18914
- const selectPackageAirportGroups = createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
18915
- const selectRoomOptionDepartureFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
18916
- const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift());
18917
- return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
18918
- });
18919
- const selectRoomOptionReturnFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
18920
- const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected));
18921
- // check if there are return flights selected
18922
- if (selectedRoomOptions?.some((ro) => ro.length > 1)) {
18923
- const roomOptions = ativeOption?.rooms.map((r) => r.options
18924
- .filter((o) => o.isSelected)
18925
- .reverse()
18926
- .shift());
18927
- return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
18928
- }
18929
- return undefined;
18930
- });
18931
- const selectApiSettings = (state) => state.apiSettings;
18932
- const selectIsUnavailable = (state) => state.booking.isUnavailable;
18933
- const selectRequestRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;
18934
- const selectOfficeId = (state) => state.booking.officeId;
18935
- const selectLanguageCode = (state) => state.booking.languageCode;
18936
- const selectCurrencyCode = (state) => state.booking.package?.currencyCode ?? 'EUR';
18937
- const selectLanguage = (state) => state.booking.languageCode;
18938
- const selectAllDynamicTranslations = (state) => state.booking.translations;
18939
- const selectDynamicTranslations = createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => {
18940
- return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {};
18941
- });
18942
- // Host override files are hand-maintained, and a null only ever blanks a key the component
18943
- // then reads, so treat it like a missing key (lodash merge already skips undefined).
18944
- const withoutNulls = (value) => isPlainObject$1(value)
18945
- ? mapValues(pickBy(value, (x) => x !== null), withoutNulls)
18946
- : value;
18947
- // getTranslations deep-merges the overrides over a clone of the built-in translations, so a
18948
- // host override of a nested group (TRAVELERS_FORM.VALIDATION, TRAVELERS_FORM.COUNTRIES)
18949
- // only replaces the keys it actually contains. Merging per section one level deep dropped
18950
- // every other built-in key of such a group, which made those keys resolve to undefined and
18951
- // crash format() in the consuming component.
18952
- const selectTranslations = createSelector([selectLanguage, selectDynamicTranslations], (language, dynamicTranslations) => getTranslations(language, isEmpty$1(dynamicTranslations) ? undefined : withoutNulls(dynamicTranslations)));
18953
- const selectBookingOptions = (state) => state.booking.bookingOptions;
18954
- const selectBookingType = (state) => state.booking.bookingType;
18955
- const selectTagIds = (state) => state.booking.tagIds;
18956
- const selectAgentAdressId = (state) => state.booking.agentAdressId;
18957
- const selectProductAttributes = (state) => state.booking.productAttributes;
18958
- const selectBookingAttributes = (state) => state.booking.bookingAttributes;
18959
- const selectBookingNumber = (state) => state.booking.bookingNumber;
18960
- const selectIsOption = (state) => state.booking.isOption;
18961
- const selectBookingRooms = (state) => state.booking.bookingAttributes?.rooms;
18962
- const selectBookingRemarks = (state) => state.booking.remarks;
18963
- const selectVoucherCodes = (state) => state.booking.voucherCodes;
18964
- const selectCalculateDeposit = (state) => state.booking.calculateDeposit;
18965
- const selectShowCommission = (state) => state.booking.showCommission;
18966
- const selectIsRetry = (state) => state.booking.isRetry;
18967
- const selectStartDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate;
18968
- const selectEndDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.toDate;
18969
- const selectAgents = (state) => state.booking.agents;
18970
- const selectCountries = (state) => state.booking.countries;
18971
- const selectProductCode = (state) => state.booking.productAttributes?.productCode;
18972
- const selectAccommodationCodes = (state) => {
18973
- const accommodationCodes = [];
18974
- state.booking.package?.options.forEach((o) => {
18975
- o.rooms.forEach((r) => r.options.forEach((ro) => {
18976
- if (!accommodationCodes.some((y) => y === ro.accommodationCode)) {
18977
- accommodationCodes.push(ro.accommodationCode);
18978
- }
18979
- }));
18980
- });
18981
- 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)))));
18982
19151
  };
18983
- const selectAccommodationViews = (state) => state.booking.accommodationViews;
18984
- const selectMappingInfo = (state) => state.booking.package?.options.find((x) => x.isSelected)?.mappingInfo;
18985
- const selectBookingQuery = (state) => {
18986
- const bookingAttributes = state.booking.bookingAttributes;
18987
- if (!bookingAttributes) {
18988
- return undefined;
18989
- }
18990
- const params = {};
18991
- Object.entries(bookingAttributes).forEach(([key, value]) => {
18992
- if (key === 'startDate' || key === 'endDate') {
18993
- value = format$2(parseISO(value), 'yyyy-MM-dd');
18994
- }
18995
- if (key === 'rooms') {
18996
- value = JsonURL.stringify(value.map((room) => omit(room, ['children'])), {
18997
- AQF: true
18998
- });
18999
- }
19000
- if (key === 'allotmentIds' && !value.length) {
19001
- value = undefined;
19002
- }
19003
- if (key === 'flight' && value) {
19004
- var flightInfo = value;
19005
- value = JsonURL.stringify(flightInfo, { AQF: true });
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);
19006
19158
  }
19007
- if (value) {
19008
- params[key] = value;
19159
+ };
19160
+ const handleClearClick = (e) => {
19161
+ if (onClear) {
19162
+ onClear();
19009
19163
  }
19010
- });
19011
- return params;
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
+ })))));
19012
19192
  };
19013
- const selectBookingQueryString = createSelector(selectBookingQuery, (params) => {
19014
- if (!params) {
19015
- return undefined;
19016
- }
19017
- return Object.keys(params)
19018
- .filter((key) => typeof params[key] !== 'undefined')
19019
- .map((key) => `${key}=${params[key]}`)
19020
- .join('&');
19021
- });
19022
- createSelector(selectTravelersFormValues, (formValues) => formValues?.mainBookerId);
19023
- const selectBookingPackagePax = createSelector(selectTravelersFormValues, (formValues) => {
19024
- var pax = [];
19025
- formValues?.rooms.forEach((r) => r.adults.forEach((x) => {
19026
- const adultPax = buildPax(x, formValues?.mainBookerId);
19027
- if (adultPax.isMainBooker) {
19028
- adultPax.mobilePhone = formValues?.phone;
19029
- adultPax.email = formValues?.email;
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;
19030
19226
  }
19031
- pax.push(adultPax);
19032
- }));
19033
- formValues?.rooms.forEach((r) => r.children.forEach((x) => {
19034
- pax.push(buildPax(x));
19035
- }));
19036
- return pax;
19037
- });
19038
- const selectBookingAddress = createSelector(selectTravelersFormValues, selectBookingPackagePax, selectBookingType, (formValues, pax, bookingType) => {
19039
- const mainBooker = pax.find((x) => x.isMainBooker);
19040
- if (!mainBooker || bookingType == 'b2b')
19041
- return undefined;
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) {
19042
19279
  return {
19043
- name: `${mainBooker.firstName} ${mainBooker.lastName}`,
19044
- street: formValues?.street,
19045
- number: formValues?.houseNumber,
19046
- box: formValues?.box,
19047
- postalCode: formValues?.zipCode,
19048
- location: formValues?.place,
19049
- country: formValues?.country,
19050
- mobilePhone: formValues?.phone,
19051
- email: formValues?.email
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 ?? []
19052
19306
  };
19053
- });
19054
- const selectBookingPackageRequest = createSelector(selectOfficeId, selectAgentId, selectAgentAdressId, (officeId, agentId, agentAdressId) => {
19055
- const agencyId = (agentId ?? agentAdressId ?? 0) > 0 ? agentId ?? agentAdressId : null;
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;
19056
19313
  return {
19057
- officeId: officeId,
19058
- agentId: agencyId,
19059
- payload: null
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 ?? ''
19060
19321
  };
19061
- });
19062
- const selectBookingPackageBookRequest = createSelector(selectBookingPackageRequest, selectBookingOptions, selectBookingType, selectBookingPackagePax, selectBookingAddress, selectPackageDetails, selectCalculateDeposit, selectShowCommission, selectAgentId, selectGeneratePaymentUrl, selectSkipPaymentWithAgent, selectNotifications, selectTagIds, selectBookingRemarks, selectVoucherCodes, (bookingPackageRequest, bookingOptions, bookingType, pax, address, packageDetails, calculateDeposit, showCommission, agentId, generatePaymentUrl, skipPaymentWithAgent, notifications, tagIds, remarks, voucherCodes) => {
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;
20140
+ value = JsonURL.stringify(flightInfo, { AQF: true });
20141
+ }
20142
+ if (value) {
20143
+ params[key] = value;
20144
+ }
20145
+ });
20146
+ return params;
20147
+ };
20148
+ const selectBookingQueryString = createSelector(selectBookingQuery, (params) => {
20149
+ if (!params) {
20150
+ return undefined;
20151
+ }
20152
+ return Object.keys(params)
20153
+ .filter((key) => typeof params[key] !== 'undefined')
20154
+ .map((key) => `${key}=${params[key]}`)
20155
+ .join('&');
20156
+ });
20157
+ createSelector(selectTravelersFormValues, (formValues) => formValues?.mainBookerId);
20158
+ const selectBookingPackagePax = createSelector(selectTravelersFormValues, (formValues) => {
20159
+ var pax = [];
20160
+ formValues?.rooms.forEach((r) => r.adults.forEach((x) => {
20161
+ const adultPax = buildPax(x, formValues?.mainBookerId);
20162
+ if (adultPax.isMainBooker) {
20163
+ adultPax.mobilePhone = formValues?.phone;
20164
+ adultPax.email = formValues?.email;
20165
+ }
20166
+ pax.push(adultPax);
20167
+ }));
20168
+ formValues?.rooms.forEach((r) => r.children.forEach((x) => {
20169
+ pax.push(buildPax(x));
20170
+ }));
20171
+ return pax;
20172
+ });
20173
+ const selectBookingAddress = createSelector(selectTravelersFormValues, selectBookingPackagePax, selectBookingType, (formValues, pax, bookingType) => {
20174
+ const mainBooker = pax.find((x) => x.isMainBooker);
20175
+ if (!mainBooker || bookingType == 'b2b')
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;
20180
+ return {
20181
+ name: `${mainBooker.firstName} ${mainBooker.lastName}`,
20182
+ street: mainBookerAddress?.street || formValues?.street,
20183
+ number: mainBookerAddress?.number || formValues?.houseNumber,
20184
+ box: formValues?.box,
20185
+ postalCode: mainBookerAddress?.postalCode || formValues?.zipCode,
20186
+ location: mainBookerAddress?.location || formValues?.place,
20187
+ country: mainBookerAddress?.country || formValues?.country,
20188
+ mobilePhone: formValues?.phone,
20189
+ email: formValues?.email
20190
+ };
20191
+ });
20192
+ const selectBookingPackageRequest = createSelector(selectOfficeId, selectAgentId, selectAgentAdressId, (officeId, agentId, agentAdressId) => {
20193
+ const agencyId = (agentId ?? agentAdressId ?? 0) > 0 ? agentId ?? agentAdressId : null;
20194
+ return {
20195
+ officeId: officeId,
20196
+ agentId: agencyId,
20197
+ payload: null
20198
+ };
20199
+ });
20200
+ const selectBookingPackageBookRequest = createSelector(selectBookingPackageRequest, selectBookingOptions, selectBookingType, selectBookingPackagePax, selectBookingAddress, selectPackageDetails, selectCalculateDeposit, selectShowCommission, selectAgentId, selectGeneratePaymentUrl, selectSkipPaymentWithAgent, selectNotifications, selectTagIds, selectBookingRemarks, selectVoucherCodes, (bookingPackageRequest, bookingOptions, bookingType, pax, address, packageDetails, calculateDeposit, showCommission, agentId, generatePaymentUrl, skipPaymentWithAgent, notifications, tagIds, remarks, voucherCodes) => {
19063
20201
  if (!packageDetails)
19064
20202
  return null;
19065
20203
  let returnPaymentUrl = false;
@@ -19122,22 +20260,11 @@ const selectBookingPackageBookRequest = createSelector(selectBookingPackageReque
19122
20260
  return bookingPackageRequest;
19123
20261
  });
19124
20262
  const selectTravelersFirstStep = (state) => state.booking.travelersFirstStep;
19125
- const buildPax = (traveler, mainBookerId) => {
19126
- return {
19127
- id: traveler.id,
19128
- gender: parseGender(traveler.gender),
19129
- firstName: traveler.firstName,
19130
- lastName: traveler.lastName,
19131
- dateOfBirth: traveler.birthDate,
19132
- age: traveler.birthDate ? null : traveler.age,
19133
- isMainBooker: traveler.id == mainBookerId,
19134
- weight: traveler.weight ?? null,
19135
- weightType: traveler.weightType ?? build.WeightType.kilograms,
19136
- height: traveler.height ?? null,
19137
- heightType: traveler.heightType ?? build.HeightType.centimeters,
19138
- customFields: traveler.customFields ?? []
19139
- };
19140
- };
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);
19141
20268
  const parseGender = (gender) => {
19142
20269
  switch (gender) {
19143
20270
  case 'm':
@@ -19645,9 +20772,7 @@ const SettingsContext = React__default.createContext({
19645
20772
  travellers: {
19646
20773
  pathSuffix: '/reizigers',
19647
20774
  travelersFirstStep: false,
19648
- showAllCountries: false,
19649
- showWeightField: false,
19650
- showHeightField: false
20775
+ showAllCountries: false
19651
20776
  },
19652
20777
  summary: {
19653
20778
  pathSuffix: '/samenvatting',
@@ -22724,1072 +23849,440 @@ const SummaryPerPaxOptionGroup = ({ group }) => {
22724
23849
  opt.line.accommodationName,
22725
23850
  !isNil(opt.line?.regimeName) && ', ',
22726
23851
  opt.line.regimeName)),
22727
- getDateText(opt.line.startDate),
22728
- opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
22729
- React__default.createElement("ul", { className: "list" }, !isEmpty$1(opt?.groups) &&
22730
- opt?.groups.map((x) => {
22731
- if (!x.options.some((y) => y.isSelected))
22732
- return;
22733
- return React__default.createElement(SummaryPerPaxOptionGroup, { group: x });
22734
- })))))));
22735
- return (React__default.createElement(React__default.Fragment, null, group.title ? (React__default.createElement("li", { className: "list__item" },
22736
- React__default.createElement("h6", null, group.title),
22737
- React__default.createElement("ul", { className: "list" }, renderOptions()))) : (React__default.createElement(React__default.Fragment, null, renderOptions()))));
22738
- };
22739
-
22740
- const SummaryBookingOptionPax = ({ pax }) => {
22741
- return (React__default.createElement(React__default.Fragment, null, pax.groups.map((y, i) => {
22742
- const paxOptions = y.options.filter((y) => y.isSelected);
22743
- if (isEmpty$1(paxOptions))
22744
- return;
22745
- return React__default.createElement(SummaryPerPaxOptionGroup, { key: i, group: y });
22746
- })));
22747
- };
22748
-
22749
- const SummaryPerUnitOptionGroup = ({ group }) => {
22750
- const groupOptions = group.options.filter((x) => x.isSelected);
22751
- const renderOptions = () => (React__default.createElement(React__default.Fragment, null, groupOptions.map((opt) => (React__default.createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
22752
- opt.line.productName,
22753
- React__default.createElement("ul", null,
22754
- React__default.createElement("li", null,
22755
- opt.line.accommodationName,
22756
- !isNil(opt.line?.regimeName) && ', ',
22757
- opt.line.regimeName)),
22758
- getDateText(opt.line.startDate),
22759
- opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
22760
- React__default.createElement("ul", null,
22761
- !isEmpty$1(opt?.groups) &&
22762
- opt?.groups.map((x) => {
22763
- if (!x.options.some((y) => y.isSelected))
22764
- return;
22765
- return React__default.createElement(SummaryPerUnitOptionGroup, { group: x });
22766
- }),
22767
- !isEmpty$1(opt?.pax) && opt?.pax.map((x) => React__default.createElement(SummaryBookingOptionPax, { pax: x }))))))));
22768
- return (React__default.createElement(React__default.Fragment, null, group.title ? (React__default.createElement("li", { className: "list__item" },
22769
- React__default.createElement("h6", null, group.title),
22770
- React__default.createElement("ul", { className: "list" }, renderOptions()))) : (React__default.createElement(React__default.Fragment, null, renderOptions()))));
22771
- };
22772
-
22773
- const SummaryBookingOptionUnit = ({ unit }) => {
22774
- return (React__default.createElement(React__default.Fragment, null, unit.groups.map((y) => {
22775
- const unitOptions = y.options.filter((y) => y.isSelected);
22776
- if (isEmpty$1(unitOptions))
22777
- return;
22778
- return React__default.createElement(SummaryPerUnitOptionGroup, { group: y });
22779
- })));
22780
- };
22781
-
22782
- const SummaryFlight = ({ flightMetaData, header }) => {
22783
- const translations = useSelector(selectTranslations);
22784
- return (React__default.createElement("li", null,
22785
- React__default.createElement("h6", null, header),
22786
- parseFlightLines(flightMetaData)?.map((flightLine) => (React__default.createElement("p", { key: flightLine.departureAirportIata },
22787
- flightLine.departureAirportDescription,
22788
- " (",
22789
- flightLine.departureAirportIata,
22790
- ") > ",
22791
- flightLine.arrivalAirportDescription,
22792
- " (",
22793
- flightLine.arrivalAirportIata,
22794
- ") ",
22795
- ' - ',
22796
- flightLine.travelClass,
22797
- " ",
22798
- ' - ',
22799
- flightLine.airlineCode,
22800
- flightLine.number,
22801
- React__default.createElement("br", null),
22802
- getDateText(flightLine.departureDate),
22803
- " ",
22804
- flightLine.departureTime,
22805
- " > ",
22806
- getDateText(flightLine.arrivalDate),
22807
- " ",
22808
- flightLine.arrivalTime,
22809
- React__default.createElement("br", null),
22810
- flightLine.airlineDescription))),
22811
- flightMetaData.luggageIncluded && React__default.createElement("p", null, translations.SIDEBAR.LUGGAGE_INCLUDED)));
22812
- };
22813
-
22814
- const SummaryPerBookingOptionGroup = ({ group }) => {
22815
- const groupOptions = group.options.filter((x) => x.isSelected);
22816
- const renderOptions = () => (React__default.createElement(React__default.Fragment, null, groupOptions.map((opt) => (React__default.createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
22817
- opt.line.productName,
22818
- React__default.createElement("ul", null,
22819
- React__default.createElement("li", null,
22820
- opt.line.accommodationName,
22821
- !isNil(opt.line?.regimeName) && ', ',
22822
- opt.line.regimeName)),
22823
- getDateText(opt.line.startDate),
22824
- opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
22825
- React__default.createElement("ul", null,
22826
- !isEmpty$1(opt?.groups) &&
22827
- opt?.groups.map((x) => {
22828
- if (!x.options.some((y) => y.isSelected))
22829
- return;
22830
- return React__default.createElement(SummaryPerBookingOptionGroup, { group: x });
22831
- }),
22832
- !isEmpty$1(opt?.units) && opt?.units.map((x) => React__default.createElement(SummaryBookingOptionUnit, { unit: x })),
22833
- !isEmpty$1(opt?.pax) && opt?.pax.map((x) => React__default.createElement(SummaryBookingOptionPax, { pax: x }))))))));
22834
- return (React__default.createElement(React__default.Fragment, null, group.title ? (React__default.createElement("li", { className: "list__item" },
22835
- React__default.createElement("h6", null, group.title),
22836
- React__default.createElement("ul", { className: "list" }, renderOptions()))) : (React__default.createElement(React__default.Fragment, null, renderOptions()))));
22837
- };
22838
-
22839
- const Summary = () => {
22840
- const dispatch = useAppDispatch();
22841
- const settings = useContext(SettingsContext);
22842
- const navigate = settings.skipRouter ? () => { } : useNavigate();
22843
- const [isSubmitting, setIsSubmitting] = useState(false);
22844
- const [checkboxes, setCheckboxes] = useState(settings.summary?.checkboxes);
22845
- const [remarks, setRemarks] = useState('');
22846
- const [voucher, setVoucher] = useState({});
22847
- const bookingQueryString = useSelector(selectBookingQueryString);
22848
- const travelersFirstStep = useSelector(selectTravelersFirstStep);
22849
- const travelerFormValues = useSelector(selectTravelersFormValues);
22850
- const packageDetails = useSelector(selectPackageDetails);
22851
- const activeOption = useSelector(selectActiveOption);
22852
- const apiSettings = useSelector(selectApiSettings);
22853
- const languageCode = useSelector(selectLanguageCode);
22854
- const officeId = useSelector(selectOfficeId);
22855
- const agentId = useSelector(selectAgentAdressId);
22856
- const departureFlight = useSelector(selectDepartureFlight);
22857
- const departureFlightMetaData = departureFlight?.flightMetaData ?? useSelector(selectRoomOptionDepartureFlightsMetaData)?.[0];
22858
- const returnFlight = useSelector(selectReturnFlight);
22859
- const returnFlightMetaData = returnFlight?.flightMetaData ?? useSelector(selectRoomOptionReturnFlightsMetaData)?.[0];
22860
- if (!travelerFormValues) {
22861
- if (settings.skipRouter) {
22862
- dispatch(setCurrentStep$1(TRAVELERS_FORM_STEP));
22863
- }
22864
- else {
22865
- navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}?${bookingQueryString}`);
22866
- }
22867
- }
22868
- const rooms = useSelector(selectRooms);
22869
- const userValidated = useSelector(selectUserValidated);
22870
- const notifications = useSelector(selectNotifications);
22871
- const bookRequest = useSelector(selectBookingPackageBookRequest);
22872
- useEffect(() => {
22873
- // Every checkbox should be checked, or no checkboxes
22874
- const checkboxesValidated = !isNil(checkboxes) ? checkboxes.every((checkbox) => checkbox.isSelected) : true;
22875
- const notificationsValidated = !isNil(notifications) ? notifications.filter((x) => x.hasToBeConfirmed).every((checkbox) => checkbox.isConfirmed) : true;
22876
- dispatch(setUserValidated(checkboxesValidated && notificationsValidated));
22877
- }, [checkboxes, notifications]);
22878
- const handleSubmit = async (e) => {
22879
- e.preventDefault();
22880
- const submitter = e.nativeEvent.submitter?.name;
22881
- setIsSubmitting(true);
22882
- if (typeof window !== 'undefined') {
22883
- window.scrollTo(0, 0);
22884
- }
22885
- const tideClientConfig = buildTideClientConfig(apiSettings);
22886
- if (!bookRequest || !tideClientConfig) {
22887
- return;
22888
- }
22889
- if (bookRequest.payload.returnPaymentUrl) {
22890
- if (typeof window !== 'undefined') {
22891
- window.scrollTo(0, 0);
22892
- if (settings.paymentRedirectUrl) {
22893
- bookRequest.payload.redirectUrl = settings.paymentRedirectUrl;
22894
- }
22895
- else {
22896
- const location = window.location;
22897
- bookRequest.payload.redirectUrl = `${location.protocol}//${location.host}${settings.basePath ?? location.pathname}?${bookingQueryString}`;
22898
- }
22899
- }
22900
- }
22901
- try {
22902
- if (submitter === 'option') {
22903
- bookRequest.payload.status = build.EntryStatus.option;
22904
- bookRequest.payload.customStatusId = settings.customOptionStatus;
22905
- dispatch(setIsOption(true));
22906
- }
22907
- const bookingResponse = await build.book(tideClientConfig, bookRequest, undefined, languageCode);
22908
- // Booking successfull
22909
- dispatch(setBookingNumber$1(bookingResponse.number));
22910
- if (bookingResponse.paymentUrl) {
22911
- const event = new CustomEvent('tide-booking-payment-url', {
22912
- detail: {
22913
- paymentUrl: bookingResponse.paymentUrl,
22914
- bookingNumber: bookingResponse.number
22915
- },
22916
- cancelable: true,
22917
- bubbles: true
22918
- });
22919
- const target = typeof document !== 'undefined' ? document.getElementById('tide-booking') ?? window : window;
22920
- const shouldAutoRedirect = target.dispatchEvent(event);
22921
- if (shouldAutoRedirect) {
22922
- window.location.href = bookingResponse.paymentUrl;
22923
- }
22924
- }
22925
- else {
22926
- if (settings.skipRouter) {
22927
- dispatch(setCurrentStep$1(CONFIRMATION_STEP));
22928
- }
22929
- else {
22930
- navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.confirmation.pathSuffix}?${bookingQueryString}`);
22931
- }
22932
- }
22933
- }
22934
- catch (error) {
22935
- if (settings.skipRouter) {
22936
- dispatch(setCurrentStep$1(ERROR_STEP));
22937
- }
22938
- else {
22939
- navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.error.pathSuffix}?${bookingQueryString}`);
22940
- }
22941
- }
22942
- finally {
22943
- setIsSubmitting(false);
22944
- }
22945
- };
22946
- const handleNotificationChange = (id, checked) => {
22947
- const updatedNotifications = notifications.map((notification) => (notification.id === id ? { ...notification, isConfirmed: checked } : notification));
22948
- dispatch(setNotifications(updatedNotifications));
22949
- };
22950
- const handleCheckboxChange = (id, checked) => {
22951
- if (isNil(checkboxes)) {
22952
- return;
22953
- }
22954
- const newCheckboxes = [...checkboxes];
22955
- const index = findIndex$1(checkboxes, (checkbox) => checkbox.id === id);
22956
- newCheckboxes[index].isSelected = !newCheckboxes[index].isSelected;
22957
- setCheckboxes(newCheckboxes);
22958
- };
22959
- const handleRemarksChange = (text) => {
22960
- dispatch(setBookingRemarks(text));
22961
- setRemarks(text);
22962
- };
22963
- const handleValidateVoucher = async () => {
22964
- if (!voucher?.code)
22965
- return;
22966
- const request = {
22967
- officeId: officeId,
22968
- agentId: agentId,
22969
- payload: {
22970
- code: voucher.code,
22971
- otherCodes: bookRequest?.payload.voucherCodes ?? []
22972
- }
22973
- };
22974
- const tideClientConfig = buildTideClientConfig(apiSettings);
22975
- if (!tideClientConfig)
22976
- return;
22977
- const result = await build.validateVoucher(tideClientConfig, request);
22978
- if (result) {
22979
- setVoucher({
22980
- ...voucher,
22981
- isValidated: true,
22982
- isValid: result.isValid
22983
- });
22984
- }
22985
- };
22986
- const handleAddVoucher = (event) => {
22987
- if (!voucher.isValid)
22988
- return;
22989
- dispatch(setVoucherCodes([...(bookRequest?.payload.voucherCodes ?? []), voucher.code ?? ''].filter((x) => x !== '')));
22990
- dispatch(fetchPriceDetails());
22991
- setVoucher({});
22992
- };
22993
- const handleRemoveVoucher = (code) => {
22994
- dispatch(setVoucherCodes(bookRequest?.payload.voucherCodes?.filter((x) => x !== code) ?? []));
22995
- dispatch(fetchPriceDetails());
22996
- };
22997
- const goPrevious = () => {
22998
- dispatch(setCurrentStep$1(travelersFirstStep ? OPTIONS_FORM_STEP : TRAVELERS_FORM_STEP));
22999
- };
23000
- const translations = useSelector(selectTranslations);
23001
- return (React__default.createElement(React__default.Fragment, null,
23002
- isSubmitting && (settings.loaderComponent || React__default.createElement(Loader, null)),
23003
- !isSubmitting && (React__default.createElement("form", { className: "form", name: "booking--summary", id: "booking--summary", onSubmit: handleSubmit },
23004
- React__default.createElement("div", { className: "form__booking--summary" },
23005
- React__default.createElement("div", { className: "form__region" },
23006
- React__default.createElement("div", { className: "form__row" },
23007
- React__default.createElement("div", { className: "form__group" },
23008
- React__default.createElement("div", { className: "form__region-header" },
23009
- React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.PERSONAL_DETAILS)))),
23010
- rooms.map((r, rIndex) => (React__default.createElement("div", { className: "form__row", key: rIndex },
23011
- React__default.createElement("div", { className: "form__group" },
23012
- React__default.createElement("div", { className: "form__region-header" },
23013
- React__default.createElement("h5", { className: "form__region-heading" }, rooms.length > 1 ? `${translations.SHARED.ROOM} ${rIndex + 1}` : translations.ROOM_OPTIONS_FORM.TRAVELER_GROUP),
23014
- React__default.createElement("p", { className: "form__region-label" }, `${r.adults.length + r.children.length} ${r.adults.length + r.children.length === 1 ? translations.SUMMARY.TRAVELER : translations.SUMMARY.TRAVELERS}: ${compact([
23015
- r.adults.length,
23016
- r.adults.length === 1 && ` ${translations.SUMMARY.ADULT}`,
23017
- r.adults.length > 1 && ` ${translations.SUMMARY.ADULTS}`,
23018
- r.adults && r.adults.length && r.children && r.children.length && ', ',
23019
- r.children.length,
23020
- r.children.length === 1 && ` ${translations.SUMMARY.CHILD}`,
23021
- r.children.length > 1 && ` ${translations.SUMMARY.CHILDREN}`
23022
- ]).join('')}`))),
23023
- [...r.adults, ...r.children].map((traveler) => {
23024
- const isMainBooker = traveler.id === travelerFormValues?.mainBookerId;
23025
- return (React__default.createElement("div", { className: "form__group form__group--sm-50", key: traveler.id },
23026
- React__default.createElement("ul", { className: "list list--plain" },
23027
- React__default.createElement("li", { className: "list__item" },
23028
- React__default.createElement("strong", null,
23029
- traveler.firstName,
23030
- " ",
23031
- traveler.lastName),
23032
- ' ',
23033
- isMainBooker && React__default.createElement("em", null,
23034
- "(",
23035
- translations.SUMMARY.MAIN_BOOKER,
23036
- ")")),
23037
- React__default.createElement("li", { className: "list__item" }, traveler.birthDate.split('-').reverse().join('/')),
23038
- isMainBooker && (React__default.createElement(React__default.Fragment, null,
23039
- travelerFormValues?.street && (React__default.createElement(React__default.Fragment, null,
23040
- React__default.createElement("li", { className: "list__item" }, `${travelerFormValues?.street} ${compact([
23041
- travelerFormValues?.houseNumber,
23042
- travelerFormValues?.box
23043
- ]).join(' ')}, ${travelerFormValues?.zipCode} ${travelerFormValues?.place}`))),
23044
- travelerFormValues?.phone && React__default.createElement("li", { className: "list__item" }, travelerFormValues?.phone),
23045
- travelerFormValues?.email && React__default.createElement("li", { className: "list__item" }, travelerFormValues?.email))))));
23046
- }))))),
23047
- React__default.createElement("div", { className: "form__region" },
23048
- React__default.createElement("div", { className: "form__row" },
23049
- React__default.createElement("div", { className: "form__group" },
23050
- React__default.createElement("div", { className: "form__region-header" },
23051
- React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.OPTIONS)))),
23052
- React__default.createElement("div", { className: "form__row" },
23053
- React__default.createElement("div", { className: "form__group" },
23054
- React__default.createElement("ul", { className: "list list--booking-summary" }, activeOption?.serviceType == build.ServiceType.flight ? (React__default.createElement(React__default.Fragment, null,
23055
- departureFlightMetaData && React__default.createElement(SummaryFlight, { flightMetaData: departureFlightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }),
23056
- returnFlightMetaData && React__default.createElement(SummaryFlight, { flightMetaData: returnFlightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT }))) : (React__default.createElement(React__default.Fragment, null, activeOption?.rooms
23057
- .flatMap((r) => r.options)
23058
- .filter((x) => x.isSelected)
23059
- .map((roomOption, roomOptionIndex) => (React__default.createElement(React__default.Fragment, { key: roomOptionIndex },
23060
- React__default.createElement("li", null,
23061
- React__default.createElement("h6", null, roomOption?.productName),
23062
- React__default.createElement("ul", null,
23063
- React__default.createElement("li", { className: "list__item" },
23064
- roomOption?.accommodationName,
23065
- !isNil(roomOption?.regimeName) && ', ',
23066
- roomOption?.regimeName)),
23067
- React__default.createElement("p", null,
23068
- "(",
23069
- roomOption?.from === roomOption?.to ? (getDateText(roomOption?.from)) : (React__default.createElement(React__default.Fragment, null,
23070
- getDateText(roomOption?.from),
23071
- " > ",
23072
- getDateText(roomOption?.to))),
23073
- ")")),
23074
- !isEmpty$1(activeOption?.groups) &&
23075
- activeOption?.groups.map((x, i) => {
23076
- if (!x.options.some((y) => y.isSelected))
23077
- return;
23078
- return React__default.createElement(SummaryPerBookingOptionGroup, { key: i, group: x });
23079
- }),
23080
- !isEmpty$1(activeOption?.optionUnits) && activeOption?.optionUnits.map((x, i) => React__default.createElement(SummaryBookingOptionUnit, { key: i, unit: x })),
23081
- !isEmpty$1(activeOption?.optionPax) && activeOption?.optionPax.map((x, i) => React__default.createElement(SummaryBookingOptionPax, { key: i, pax: x })),
23082
- packageDetails?.outwardFlights &&
23083
- packageDetails.outwardFlights
23084
- .filter((x) => x.isSelected)
23085
- .map((flight, i) => (React__default.createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }))),
23086
- packageDetails?.returnFlights &&
23087
- packageDetails.returnFlights
23088
- .filter((x) => x.isSelected)
23089
- .map((flight, i) => (React__default.createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT })))))))))))),
23090
- settings.enableVoucher && (React__default.createElement("div", { className: "form__region" },
23091
- React__default.createElement("div", { className: "form__row" },
23092
- React__default.createElement("div", { className: "form__group" },
23093
- React__default.createElement("div", { className: "form__region-header" },
23094
- React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.VOUCHERS)))),
23095
- React__default.createElement("div", { className: "form__row" },
23096
- React__default.createElement("div", { className: "form__group" },
23097
- React__default.createElement("input", { type: "text", className: "form__input info-message__voucher__input", defaultValue: voucher.code, onChange: (e) => setVoucher({ code: e.target.value }) }),
23098
- React__default.createElement("button", { type: "button", className: buildClassName(['cta', !voucher.code && 'cta--disabled']), onClick: (e) => handleValidateVoucher() }, translations.SUMMARY.VOUCHER_VALIDATE))),
23099
- React__default.createElement("div", { className: "form__row" },
23100
- React__default.createElement("div", { className: "form__group info-message__voucher" },
23101
- voucher.isValid && voucher.isValidated && (React__default.createElement("div", { className: "info-message info-message__voucher__valid" },
23102
- React__default.createElement("span", null, translations.SUMMARY.VOUCHER_VALID),
23103
- React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: handleAddVoucher }, translations.SUMMARY.ADD_VOUCHER))),
23104
- !voucher.isValid && voucher.isValidated && React__default.createElement("div", { className: "info-message--error" }, translations.SUMMARY.VOUCHER_INVALID))),
23105
- React__default.createElement("div", { className: "form__row" },
23106
- React__default.createElement("ul", { className: "info-message__voucher" }, !isEmpty$1(bookRequest?.payload.voucherCodes) &&
23107
- bookRequest?.payload.voucherCodes?.map((y, i) => (React__default.createElement("li", { key: i },
23108
- React__default.createElement("div", { className: "info-message__voucher__list" },
23109
- y,
23110
- ' ',
23111
- React__default.createElement("button", { type: "button", className: "cta--add-remove", onClick: (e) => handleRemoveVoucher(y) },
23112
- React__default.createElement(Icon, { height: 16, name: "ui-trashcan" })))))))))),
23113
- !isEmpty$1(notifications) && (React__default.createElement("div", { className: "form__region" },
23114
- React__default.createElement("div", { className: "form__row" },
23115
- React__default.createElement("div", { className: "form__group" },
23116
- React__default.createElement("div", { className: "info-message" },
23117
- React__default.createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
23118
- React__default.createElement("div", { className: "info-message__copy" },
23119
- React__default.createElement("h5", null, translations.SUMMARY.NOTIFICATIONS_TITLE),
23120
- React__default.createElement(React__default.Fragment, null,
23121
- uniqBy(notifications.filter((x) => !x.hasToBeConfirmed), 'id').map((notification) => (React__default.createElement("span", { key: notification.id, className: "checkbox__label-text" },
23122
- React__default.createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
23123
- React__default.createElement("span", { className: "checkbox__label-text--description" }, notification.description)))),
23124
- uniqBy(notifications.filter((x) => x.hasToBeConfirmed), 'id').map((notification) => (React__default.createElement("div", { className: "checkbox", key: notification.id },
23125
- React__default.createElement("label", { className: "checkbox__label" },
23126
- React__default.createElement("input", { type: "checkbox", className: "checkbox__input", checked: notification.isConfirmed, onChange: (e) => handleNotificationChange(notification.id, e.target.checked) }),
23127
- React__default.createElement("span", { className: "checkbox__label-text" },
23128
- React__default.createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
23129
- React__default.createElement("span", { className: "checkbox__label-text--description" }, notification.description))))))))))))),
23130
- React__default.createElement("div", { className: "form__region" },
23131
- React__default.createElement("div", { className: "form__row" },
23132
- React__default.createElement("div", { className: "form__group" },
23133
- React__default.createElement("div", { className: "form__region-header" },
23134
- React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.REMARKS)))),
23135
- React__default.createElement("div", { className: "form__row" },
23136
- React__default.createElement("div", { className: "form__group" },
23137
- React__default.createElement("textarea", { className: "form__input", defaultValue: remarks, onChange: (e) => handleRemarksChange(e.target.value) })))),
23138
- React__default.createElement("div", { className: "form__region" },
23139
- React__default.createElement("div", { className: "form__row" },
23140
- React__default.createElement("div", { className: "form__group" },
23141
- React__default.createElement("div", { className: "info-message" },
23142
- React__default.createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
23143
- React__default.createElement("div", { className: "info-message__copy" },
23144
- React__default.createElement("h5", null, translations.SUMMARY.VALIDATE_TITLE),
23145
- settings.customValidateText ? (React__default.createElement("div", { dangerouslySetInnerHTML: {
23146
- __html: settings.customValidateText
23147
- } })) : (React__default.createElement(React__default.Fragment, null,
23148
- React__default.createElement("p", null, settings.isOffer ? translations.SUMMARY.VALIDATE_TEXT_OFFER : translations.SUMMARY.VALIDATE_TEXT_BOOKING),
23149
- settings.allowOption && React__default.createElement("p", null, translations.SUMMARY.VALIDATE_TEXT_OPTION))),
23150
- checkboxes &&
23151
- checkboxes.map((checkbox) => (React__default.createElement("div", { className: "checkbox", key: checkbox.id },
23152
- React__default.createElement("label", { className: "checkbox__label" },
23153
- React__default.createElement("input", { type: "checkbox", className: "checkbox__input", checked: checkbox.isSelected, onChange: (e) => handleCheckboxChange(checkbox.id, e.target.checked) }),
23154
- React__default.createElement("span", { className: "checkbox__label-text", dangerouslySetInnerHTML: {
23155
- __html: checkbox.text
23156
- } }))))))))))),
23157
- React__default.createElement("div", { className: "booking__navigator" },
23158
- 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)),
23159
- 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)),
23160
- 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))))));
23852
+ getDateText(opt.line.startDate),
23853
+ opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
23854
+ React__default.createElement("ul", { className: "list" }, !isEmpty$1(opt?.groups) &&
23855
+ opt?.groups.map((x) => {
23856
+ if (!x.options.some((y) => y.isSelected))
23857
+ return;
23858
+ return React__default.createElement(SummaryPerPaxOptionGroup, { group: x });
23859
+ })))))));
23860
+ return (React__default.createElement(React__default.Fragment, null, group.title ? (React__default.createElement("li", { className: "list__item" },
23861
+ React__default.createElement("h6", null, group.title),
23862
+ React__default.createElement("ul", { className: "list" }, renderOptions()))) : (React__default.createElement(React__default.Fragment, null, renderOptions()))));
23161
23863
  };
23162
23864
 
23163
- const LabeledInput = ({ name, type, required, value, defaultValue, min, max, label, placeholder, extraClassName, hasError, onChange, onBlur }) => {
23164
- return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23165
- React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
23166
- 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 })));
23865
+ const SummaryBookingOptionPax = ({ pax }) => {
23866
+ return (React__default.createElement(React__default.Fragment, null, pax.groups.map((y, i) => {
23867
+ const paxOptions = y.options.filter((y) => y.isSelected);
23868
+ if (isEmpty$1(paxOptions))
23869
+ return;
23870
+ return React__default.createElement(SummaryPerPaxOptionGroup, { key: i, group: y });
23871
+ })));
23167
23872
  };
23168
23873
 
23169
- const LabeledSelect = ({ name, id, required, value, defaultValue, label, extraClassName, extraDropdownClassName, hasError, options, onChange, onBlur }) => {
23170
- return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23171
- React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
23172
- React__default.createElement("div", { className: buildClassName(['dropdown', extraDropdownClassName]) },
23173
- 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)))))));
23874
+ const SummaryPerUnitOptionGroup = ({ group }) => {
23875
+ const groupOptions = group.options.filter((x) => x.isSelected);
23876
+ const renderOptions = () => (React__default.createElement(React__default.Fragment, null, groupOptions.map((opt) => (React__default.createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
23877
+ opt.line.productName,
23878
+ React__default.createElement("ul", null,
23879
+ React__default.createElement("li", null,
23880
+ opt.line.accommodationName,
23881
+ !isNil(opt.line?.regimeName) && ', ',
23882
+ opt.line.regimeName)),
23883
+ getDateText(opt.line.startDate),
23884
+ opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
23885
+ React__default.createElement("ul", null,
23886
+ !isEmpty$1(opt?.groups) &&
23887
+ opt?.groups.map((x) => {
23888
+ if (!x.options.some((y) => y.isSelected))
23889
+ return;
23890
+ return React__default.createElement(SummaryPerUnitOptionGroup, { group: x });
23891
+ }),
23892
+ !isEmpty$1(opt?.pax) && opt?.pax.map((x) => React__default.createElement(SummaryBookingOptionPax, { pax: x }))))))));
23893
+ return (React__default.createElement(React__default.Fragment, null, group.title ? (React__default.createElement("li", { className: "list__item" },
23894
+ React__default.createElement("h6", null, group.title),
23895
+ React__default.createElement("ul", { className: "list" }, renderOptions()))) : (React__default.createElement(React__default.Fragment, null, renderOptions()))));
23174
23896
  };
23175
23897
 
23176
- const normalize = (v) => (v == null ? '' : String(v));
23177
- const parseCombined = (raw, countries) => {
23178
- const value = normalize(raw).trim();
23179
- if (!value)
23180
- return { prefix: '', number: '' };
23181
- // Try to match a known prefix at the start. Prefer longest match.
23182
- const sorted = [...countries].sort((a, b) => b.phonePrefix.length - a.phonePrefix.length);
23183
- const hit = sorted.find((c) => value.startsWith(c.phonePrefix));
23184
- if (hit) {
23185
- const rest = value
23186
- .slice(hit.phonePrefix.length)
23187
- .trim()
23188
- .replace(/^[-\s]+/, '');
23189
- return { prefix: hit.phonePrefix, number: rest };
23190
- }
23191
- // Fallback: split on first space if it looks like a +prefix number
23192
- const m = value.match(/^(\+\d{1,4})[\s-]*(.*)$/);
23193
- if (m)
23194
- return { prefix: m[1], number: m[2] };
23195
- return { prefix: '', number: value };
23196
- };
23197
- const PhoneInput = ({ name, required, value, label, placeholder, extraClassName, hasError, countries, countryIso2, onChange, onBlur }) => {
23198
- // Derive initial state from `value`
23199
- const initialCountry = countries.find((c) => c.iso2 === countryIso2);
23200
- const initial = useMemo(() => parseCombined(normalize(value), countries), [value, countries]);
23201
- const [prefix, setPrefix] = useState(initial.prefix);
23202
- const [number, setNumber] = useState(initial.number);
23203
- // Keep state in sync if the parent changes `value`
23204
- useEffect(() => {
23205
- const parsed = !prefix && !number && initialCountry ? { prefix: initialCountry.phonePrefix, number: '' } : parseCombined(normalize(value), countries);
23206
- if (parsed.prefix)
23207
- setPrefix(parsed.prefix);
23208
- if (parsed.number)
23209
- setNumber(parsed.number);
23210
- }, [value, countries, countryIso2]);
23211
- const emitCombinedChange = (e, nextPrefix, nextNumber) => {
23212
- // Combine with a space, unless the number already starts with a dash or space
23213
- const combined = nextPrefix && nextNumber ? compact([nextPrefix, nextNumber]).join(' ') : null;
23214
- onChange?.({
23215
- ...e,
23216
- type: 'change',
23217
- target: { name, value: combined },
23218
- currentTarget: { name, value: combined }
23219
- });
23220
- };
23221
- const handleCountryBlur = (e) => {
23222
- onBlur?.(e);
23223
- };
23224
- const onCountryChange = (e) => {
23225
- const next = e.target.value;
23226
- setPrefix(next);
23227
- emitCombinedChange(e, next, number);
23228
- };
23229
- const onPhoneChange = (e) => {
23230
- const cleaned = e.target.value.replace(/[^\d\s-]/g, '');
23231
- setNumber(cleaned);
23232
- emitCombinedChange(e, prefix, cleaned);
23233
- };
23234
- const handleNumberBlur = (e) => {
23235
- onBlur?.(e);
23236
- };
23237
- // Ensure the select shows a reasonable default when prefix is empty
23238
- const selectValue = prefix || '';
23239
- return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23240
- label && React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
23241
- React__default.createElement("div", { className: "phone-input" },
23242
- React__default.createElement("div", { className: "dropdown" },
23243
- React__default.createElement("select", { "aria-label": label ? `${label} – country code` : 'Country calling code', name: `${name}__country`, value: selectValue, onBlur: handleCountryBlur, onChange: onCountryChange },
23244
- React__default.createElement("option", { value: "", disabled: true }),
23245
- countries?.map((option) => (React__default.createElement("option", { key: option.iso2, value: option.phonePrefix },
23246
- option.name,
23247
- " (",
23248
- option.phonePrefix,
23249
- ")"))))),
23250
- 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 }),
23251
- React__default.createElement("input", { type: "hidden", name: name, value: compact([prefix, number]).join(' ') }))));
23898
+ const SummaryBookingOptionUnit = ({ unit }) => {
23899
+ return (React__default.createElement(React__default.Fragment, null, unit.groups.map((y) => {
23900
+ const unitOptions = y.options.filter((y) => y.isSelected);
23901
+ if (isEmpty$1(unitOptions))
23902
+ return;
23903
+ return React__default.createElement(SummaryPerUnitOptionGroup, { group: y });
23904
+ })));
23252
23905
  };
23253
23906
 
23254
- const hasVisibleError = (formik, key) => get(formik.errors, key) && get(formik.touched, key);
23907
+ const SummaryFlight = ({ flightMetaData, header }) => {
23908
+ const translations = useSelector(selectTranslations);
23909
+ return (React__default.createElement("li", null,
23910
+ React__default.createElement("h6", null, header),
23911
+ parseFlightLines(flightMetaData)?.map((flightLine) => (React__default.createElement("p", { key: flightLine.departureAirportIata },
23912
+ flightLine.departureAirportDescription,
23913
+ " (",
23914
+ flightLine.departureAirportIata,
23915
+ ") > ",
23916
+ flightLine.arrivalAirportDescription,
23917
+ " (",
23918
+ flightLine.arrivalAirportIata,
23919
+ ") ",
23920
+ ' - ',
23921
+ flightLine.travelClass,
23922
+ " ",
23923
+ ' - ',
23924
+ flightLine.airlineCode,
23925
+ flightLine.number,
23926
+ React__default.createElement("br", null),
23927
+ getDateText(flightLine.departureDate),
23928
+ " ",
23929
+ flightLine.departureTime,
23930
+ " > ",
23931
+ getDateText(flightLine.arrivalDate),
23932
+ " ",
23933
+ flightLine.arrivalTime,
23934
+ React__default.createElement("br", null),
23935
+ flightLine.airlineDescription))),
23936
+ flightMetaData.luggageIncluded && React__default.createElement("p", null, translations.SIDEBAR.LUGGAGE_INCLUDED)));
23937
+ };
23255
23938
 
23256
- const GenderControl = ({ translations, value, formik, name }) => {
23257
- return (React__default.createElement("div", { className: buildClassName(['form__group', hasVisibleError(formik, name) && 'form__group--error']) },
23258
- React__default.createElement("label", { className: "form__label" },
23259
- translations.TRAVELERS_FORM.GENDER_ID,
23260
- " *"),
23261
- React__default.createElement("div", { className: "radiobutton-group" },
23262
- React__default.createElement("div", { className: "radiobutton" },
23263
- React__default.createElement("label", { className: "radiobutton__label" },
23264
- React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "m", checked: value.gender === 'm' }),
23265
- translations.TRAVELERS_FORM.MALE_GENDER)),
23266
- React__default.createElement("div", { className: "radiobutton" },
23267
- React__default.createElement("label", { className: "radiobutton__label" },
23268
- React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "f", checked: value.gender === 'f' }),
23269
- translations.TRAVELERS_FORM.FEMALE_GENDER)))));
23939
+ const SummaryPerBookingOptionGroup = ({ group }) => {
23940
+ const groupOptions = group.options.filter((x) => x.isSelected);
23941
+ const renderOptions = () => (React__default.createElement(React__default.Fragment, null, groupOptions.map((opt) => (React__default.createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
23942
+ opt.line.productName,
23943
+ React__default.createElement("ul", null,
23944
+ React__default.createElement("li", null,
23945
+ opt.line.accommodationName,
23946
+ !isNil(opt.line?.regimeName) && ', ',
23947
+ opt.line.regimeName)),
23948
+ getDateText(opt.line.startDate),
23949
+ opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
23950
+ React__default.createElement("ul", null,
23951
+ !isEmpty$1(opt?.groups) &&
23952
+ opt?.groups.map((x) => {
23953
+ if (!x.options.some((y) => y.isSelected))
23954
+ return;
23955
+ return React__default.createElement(SummaryPerBookingOptionGroup, { group: x });
23956
+ }),
23957
+ !isEmpty$1(opt?.units) && opt?.units.map((x) => React__default.createElement(SummaryBookingOptionUnit, { unit: x })),
23958
+ !isEmpty$1(opt?.pax) && opt?.pax.map((x) => React__default.createElement(SummaryBookingOptionPax, { pax: x }))))))));
23959
+ return (React__default.createElement(React__default.Fragment, null, group.title ? (React__default.createElement("li", { className: "list__item" },
23960
+ React__default.createElement("h6", null, group.title),
23961
+ React__default.createElement("ul", { className: "list" }, renderOptions()))) : (React__default.createElement(React__default.Fragment, null, renderOptions()))));
23270
23962
  };
23271
23963
 
23272
- const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect, onClear }) => {
23273
- const [showItems, setShowItems] = useState(false);
23274
- const handleChange = (e) => {
23275
- if (onChange) {
23276
- onChange(e.target.value);
23964
+ const Summary = () => {
23965
+ const dispatch = useAppDispatch();
23966
+ const settings = useContext(SettingsContext);
23967
+ const navigate = settings.skipRouter ? () => { } : useNavigate();
23968
+ const [isSubmitting, setIsSubmitting] = useState(false);
23969
+ const [checkboxes, setCheckboxes] = useState(settings.summary?.checkboxes);
23970
+ const [remarks, setRemarks] = useState('');
23971
+ const [voucher, setVoucher] = useState({});
23972
+ const bookingQueryString = useSelector(selectBookingQueryString);
23973
+ const travelersFirstStep = useSelector(selectTravelersFirstStep);
23974
+ const travelerFormValues = useSelector(selectTravelersFormValues);
23975
+ const packageDetails = useSelector(selectPackageDetails);
23976
+ const activeOption = useSelector(selectActiveOption);
23977
+ const apiSettings = useSelector(selectApiSettings);
23978
+ const languageCode = useSelector(selectLanguageCode);
23979
+ const officeId = useSelector(selectOfficeId);
23980
+ const agentId = useSelector(selectAgentAdressId);
23981
+ const departureFlight = useSelector(selectDepartureFlight);
23982
+ const departureFlightMetaData = departureFlight?.flightMetaData ?? useSelector(selectRoomOptionDepartureFlightsMetaData)?.[0];
23983
+ const returnFlight = useSelector(selectReturnFlight);
23984
+ const returnFlightMetaData = returnFlight?.flightMetaData ?? useSelector(selectRoomOptionReturnFlightsMetaData)?.[0];
23985
+ if (!travelerFormValues) {
23986
+ if (settings.skipRouter) {
23987
+ dispatch(setCurrentStep$1(TRAVELERS_FORM_STEP));
23277
23988
  }
23278
- };
23279
- const handleClearClick = (e) => {
23280
- if (onClear) {
23281
- onClear();
23989
+ else {
23990
+ navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}?${bookingQueryString}`);
23282
23991
  }
23283
- setShowItems(false);
23284
- };
23285
- return (React__default.createElement("div", { className: "typeahead" },
23286
- React__default.createElement("div", { className: "typeahead__trigger typeahead__trigger--icon" },
23287
- React__default.createElement(Icon, { name: "ui-qsm-location" }),
23288
- React__default.createElement("input", { type: "text", className: "form__input", name: name, value: value, onChange: handleChange, placeholder: placeholder, required: true, autoComplete: "off", onKeyDown: (e) => {
23289
- if (e.key === 'Tab' && options && onSelect) {
23290
- onSelect(options[0].key);
23291
- setShowItems(false);
23292
- }
23293
- }, onFocus: () => setShowItems(true) }),
23294
- React__default.createElement("button", { type: "button", className: "button button--clear", onClick: handleClearClick })),
23295
- options && options.length > 0 && showItems && (React__default.createElement("div", { className: "typeahead__options" }, (options ?? []).map((option) => {
23296
- const matchIndex = typeof value !== 'undefined' ? option.text.indexOf(value) : 0;
23297
- const prefix = option.text.slice(0, matchIndex);
23298
- const highlight = option.text.slice(matchIndex, value?.length ?? 0);
23299
- const suffix = option.text.slice(matchIndex);
23300
- return (React__default.createElement("div", { className: "typeahead__option", key: option.key },
23301
- React__default.createElement("div", { className: "typeahead__option-header" },
23302
- React__default.createElement("button", { onClick: () => {
23303
- if (onSelect)
23304
- onSelect(option.key);
23305
- setShowItems(false);
23306
- }, type: "button", className: "button button--plain" },
23307
- prefix,
23308
- React__default.createElement("span", { className: "typeahead__option-highlight" }, highlight),
23309
- suffix))));
23310
- })))));
23311
- };
23312
-
23313
- // A traveler is a baby when the age they were booked with is at or below the baby
23314
- // boundary. Babies get their own label and birth date validation (TIDE-3269). Only
23315
- // meaningful for the regular form: the compact form asks for an age instead of a birth
23316
- // date, so it has nothing to validate and its own age input already states the age.
23317
- function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
23318
- return typeof age === 'number' && age <= maxBabyAge;
23319
- }
23320
- // The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
23321
- function formatBirthDate$1(birthDate) {
23322
- if (!birthDate)
23323
- return '';
23324
- const parsed = parse(birthDate, 'yyyy-MM-dd', new Date());
23325
- return isValid(parsed) ? format$2(parsed, 'dd-MM-yyyy') : '';
23326
- }
23327
- // A customFieldConfigurationId can have separate "main-booker"/other-pax variants; resolved per traveler below.
23328
- const MAIN_BOOKER_GROUP = 'main-booker';
23329
- function buildEmptyCustomFields(paxCustomFields = []) {
23330
- const uniqueIds = Array.from(new Set(paxCustomFields.map((field) => field.customFieldConfigurationId)));
23331
- return uniqueIds.map((customFieldConfigurationId) => ({ customFieldConfigurationId, value: '' }));
23332
- }
23333
- function getApplicablePaxCustomFields(paxCustomFields = [], isMainBooker) {
23334
- const variantsById = new Map();
23335
- paxCustomFields.forEach((field) => {
23336
- const variants = variantsById.get(field.customFieldConfigurationId) ?? [];
23337
- variants.push(field);
23338
- variantsById.set(field.customFieldConfigurationId, variants);
23339
- });
23340
- const applicableFields = [];
23341
- variantsById.forEach((variants) => {
23342
- const hasRoleSplit = variants.some((v) => v.group === MAIN_BOOKER_GROUP);
23343
- if (!hasRoleSplit) {
23344
- applicableFields.push(variants[0]);
23992
+ }
23993
+ const rooms = useSelector(selectRooms);
23994
+ const userValidated = useSelector(selectUserValidated);
23995
+ const notifications = useSelector(selectNotifications);
23996
+ const bookRequest = useSelector(selectBookingPackageBookRequest);
23997
+ useEffect(() => {
23998
+ // Every checkbox should be checked, or no checkboxes
23999
+ const checkboxesValidated = !isNil(checkboxes) ? checkboxes.every((checkbox) => checkbox.isSelected) : true;
24000
+ const notificationsValidated = !isNil(notifications) ? notifications.filter((x) => x.hasToBeConfirmed).every((checkbox) => checkbox.isConfirmed) : true;
24001
+ dispatch(setUserValidated(checkboxesValidated && notificationsValidated));
24002
+ }, [checkboxes, notifications]);
24003
+ const handleSubmit = async (e) => {
24004
+ e.preventDefault();
24005
+ const submitter = e.nativeEvent.submitter?.name;
24006
+ setIsSubmitting(true);
24007
+ if (typeof window !== 'undefined') {
24008
+ window.scrollTo(0, 0);
24009
+ }
24010
+ const tideClientConfig = buildTideClientConfig(apiSettings);
24011
+ if (!bookRequest || !tideClientConfig) {
23345
24012
  return;
23346
24013
  }
23347
- const roleMatch = variants.find((v) => (v.group === MAIN_BOOKER_GROUP) === isMainBooker);
23348
- if (roleMatch)
23349
- applicableFields.push(roleMatch);
23350
- });
23351
- return applicableFields;
23352
- }
23353
- function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxCustomFields) {
23354
- if (isCompact) {
23355
- return {
23356
- id: traveler.id,
23357
- firstName: personTranslation,
23358
- lastName: `${followNumber.number++}`,
23359
- birthDate: '',
23360
- gender: '',
23361
- // Keep a booked age of 0 instead of submitting and pricing the traveler as a 30 year
23362
- // old, while still falling back for a missing or unusable age. `??` alone would let a
23363
- // NaN (unparseable date of birth upstream) reach the age input and the booked pax.
23364
- // See TIDE-3269.
23365
- age: Number.isFinite(traveler.age) ? traveler.age : 30,
23366
- // No isBaby here on purpose: the age below is editable and nothing revalidates it
23367
- // in compact mode, so a stored flag would end up contradicting the entered age.
23368
- weight: null,
23369
- weightType: build.WeightType.kilograms,
23370
- height: null,
23371
- heightType: build.HeightType.centimeters,
23372
- customFields: buildEmptyCustomFields(paxCustomFields)
23373
- };
23374
- }
23375
- return {
23376
- id: traveler.id,
23377
- firstName: '',
23378
- lastName: '',
23379
- birthDate: '',
23380
- gender: '',
23381
- isBaby: isBabyAge(traveler.age, maxBabyAge),
23382
- weight: null,
23383
- weightType: build.WeightType.kilograms,
23384
- height: null,
23385
- heightType: build.HeightType.centimeters,
23386
- customFields: buildEmptyCustomFields(paxCustomFields)
23387
- };
23388
- }
23389
- function createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, personTranslation, isCompact, maxBabyAge, paxCustomFields) {
23390
- const followNumber = { number: 1 };
23391
- const initialValues = {
23392
- startDate,
23393
- endDate,
23394
- rooms: formRooms.map((room) => ({
23395
- adults: room.adults.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxCustomFields)),
23396
- children: room.children.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxCustomFields))
23397
- })),
23398
- mainBookerId: -1,
23399
- street: '',
23400
- houseNumber: '',
23401
- box: '',
23402
- zipCode: '',
23403
- place: '',
23404
- country: '',
23405
- phone: '',
23406
- email: '',
23407
- emailConfirmation: '',
23408
- travelAgentId: agentAdressId ?? 0,
23409
- travelAgentName: ''
23410
- };
23411
- if (initialValues.rooms?.[0]?.adults?.[0]) {
23412
- initialValues.mainBookerId = initialValues.rooms[0].adults[0].id;
23413
- }
23414
- return initialValues;
23415
- }
23416
- function createInitialValuesFromEditablePackagingEntry(editablePackagingEntry, agentAdressId) {
23417
- const pax = editablePackagingEntry?.pax ?? [];
23418
- const rooms = editablePackagingEntry.rooms.map((room) => {
23419
- const roomPax = pax.filter((x) => room.paxIds.includes(x.id));
23420
- const adults = roomPax.filter((x) => x.age >= 18);
23421
- const children = roomPax.filter((x) => x.age < 18);
23422
- return {
23423
- adults: adults.map((roomTraveler) => {
23424
- return {
23425
- id: roomTraveler.id,
23426
- firstName: roomTraveler.firstName ?? '',
23427
- lastName: roomTraveler.lastName ?? '',
23428
- birthDate: roomTraveler.dateOfBirth ? format$2(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
23429
- gender: ''
23430
- };
23431
- }),
23432
- children: children.map((roomTraveler) => {
23433
- return {
23434
- id: roomTraveler.id,
23435
- firstName: roomTraveler.firstName ?? '',
23436
- lastName: roomTraveler.lastName ?? '',
23437
- birthDate: roomTraveler.dateOfBirth ? format$2(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
23438
- gender: '',
23439
- isBaby: isBabyAge(roomTraveler.age)
23440
- };
23441
- })
23442
- };
23443
- });
23444
- const values = createInitialValuesFromRooms(rooms.map((room) => ({ adults: room.adults, children: room.children })), first(editablePackagingEntry?.lines)?.from, last(editablePackagingEntry?.lines)?.to, agentAdressId);
23445
- values.rooms = rooms;
23446
- values.mainBookerId = pax.find((x) => x.isMainBooker)?.id ?? rooms[0]?.adults?.[0]?.id ?? -1;
23447
- const address = editablePackagingEntry?.address;
23448
- values.street = address?.street ?? '';
23449
- values.houseNumber = address?.houseNumber ?? '';
23450
- values.box = address?.box ?? '';
23451
- values.zipCode = address?.zipCode ?? '';
23452
- values.place = address?.place ?? '';
23453
- values.country = address?.country ?? '';
23454
- values.phone = address?.phone ?? '';
23455
- values.email = address?.email ?? '';
23456
- values.emailConfirmation = address?.email ?? '';
23457
- values.travelAgentId = address?.travelAgentId ?? agentAdressId ?? 0;
23458
- return values;
23459
- }
23460
- function applyTravelersFormValuesToEditablePackagingEntry(editablePackagingEntry, values) {
23461
- const travelers = values.rooms.flatMap((room) => [...room.adults, ...room.children]);
23462
- return {
23463
- ...editablePackagingEntry,
23464
- pax: (editablePackagingEntry.pax ?? []).map((pax) => {
23465
- const traveler = travelers.find((x) => x.id === pax.id);
23466
- if (!traveler)
23467
- return pax;
23468
- return {
23469
- ...pax,
23470
- firstName: traveler.firstName ?? '',
23471
- lastName: traveler.lastName ?? '',
23472
- dateOfBirth: traveler.birthDate || null,
23473
- isMainBooker: traveler.id === values.mainBookerId
23474
- };
23475
- }),
23476
- address: {
23477
- ...editablePackagingEntry.address,
23478
- street: values.street,
23479
- houseNumber: values.houseNumber,
23480
- box: values.box,
23481
- zipCode: values.zipCode,
23482
- place: values.place,
23483
- country: values.country,
23484
- travelAgentId: values.travelAgentId,
23485
- phone: values.phone,
23486
- email: values.email
24014
+ if (bookRequest.payload.returnPaymentUrl) {
24015
+ if (typeof window !== 'undefined') {
24016
+ window.scrollTo(0, 0);
24017
+ if (settings.paymentRedirectUrl) {
24018
+ bookRequest.payload.redirectUrl = settings.paymentRedirectUrl;
24019
+ }
24020
+ else {
24021
+ const location = window.location;
24022
+ bookRequest.payload.redirectUrl = `${location.protocol}//${location.host}${settings.basePath ?? location.pathname}?${bookingQueryString}`;
24023
+ }
24024
+ }
23487
24025
  }
23488
- };
23489
- }
23490
- const SharedTravelersForm = ({ formik, translations, travellersSettings, countries, agents, bookingType, agentAdressId, travelersFirstStep = false, isUnavailable = false, useCompactForm = false, showAllCountries = false, showWeightField = false, showHeightField = false, paxCustomFields, showAgentSelection = false, initialShowAgents = false, renderPreviousButton, onBookingTypeChange }) => {
23491
- const [showAgents, setShowAgents] = useState(initialShowAgents);
23492
- const typeaheadAgents = useMemo(() => sortBy(agents?.map((agent) => ({
23493
- key: `${agent.id}`,
23494
- value: `${agent.name}${agent.postalCode || agent.location ? ` (${compact([agent.postalCode, agent.location]).join(' ')})` : ''}`,
23495
- text: `${agent.name}${agent.postalCode || agent.location ? ` (${compact([agent.postalCode, agent.location]).join(' ')})` : ''}`
23496
- })), 'value') ?? [], [agents]);
23497
- const [filteredAgents, setFilteredAgents] = useState(typeaheadAgents);
23498
- const flatErrors = flat(formik.errors);
23499
- const errorKeys = Object.keys(flatErrors).filter((key) => get(formik.touched, key));
23500
- const hasVisibleError = (key) => get(formik.errors, key) && get(formik.touched, key);
23501
- const mainBooker = formik.values.rooms
23502
- .find((room) => room.adults.find((traveler) => traveler.id === formik.values.mainBookerId))
23503
- ?.adults.find((traveler) => traveler.id === formik.values.mainBookerId);
23504
- const countryOptions = [
23505
- { key: 'empty', value: undefined, label: translations.TRAVELERS_FORM.SELECT_COUNTRY },
23506
- ...(showAllCountries
23507
- ? countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? []
23508
- : travellersSettings?.countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? [
23509
- { key: 'be', value: 'be', label: translations.TRAVELERS_FORM.COUNTRIES.BELGIUM },
23510
- { key: 'nl', value: 'nl', label: translations.TRAVELERS_FORM.COUNTRIES.NETHERLANDS },
23511
- { key: 'fr', value: 'fr', label: translations.TRAVELERS_FORM.COUNTRIES.FRANCE }
23512
- ])
23513
- ];
23514
- const handleMainBookerChange = (event) => {
23515
- formik.setFieldValue('mainBookerId', parseInt(event.currentTarget.value, 10));
23516
- };
23517
- const handleAgentChange = (value) => {
23518
- setFilteredAgents(typeaheadAgents.filter((x) => x.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1));
23519
- formik.setFieldValue('travelAgentName', value);
23520
- };
23521
- const handleAgentSelect = (key) => {
23522
- const agent = typeaheadAgents.find((x) => x.key === key);
23523
- formik.setValues({
23524
- ...formik.values,
23525
- travelAgentId: Number(agent?.key),
23526
- travelAgentName: agent?.value ?? ''
23527
- });
23528
- onBookingTypeChange?.(agentAdressId && agentAdressId !== 0 ? 'b2b' : 'b2b2c');
23529
- };
23530
- const handleAgentClear = () => {
23531
- formik.setValues({ ...formik.values, travelAgentId: 0, travelAgentName: '' });
23532
- onBookingTypeChange?.('b2c');
23533
- };
23534
- const toggleAgent = (value) => {
23535
- setShowAgents(value);
23536
- if (!value) {
23537
- handleAgentClear();
23538
- setFilteredAgents([]);
24026
+ try {
24027
+ if (submitter === 'option') {
24028
+ bookRequest.payload.status = build.EntryStatus.option;
24029
+ bookRequest.payload.customStatusId = settings.customOptionStatus;
24030
+ dispatch(setIsOption(true));
24031
+ }
24032
+ const bookingResponse = await build.book(tideClientConfig, bookRequest, undefined, languageCode);
24033
+ // Booking successfull
24034
+ dispatch(setBookingNumber$1(bookingResponse.number));
24035
+ if (bookingResponse.paymentUrl) {
24036
+ const event = new CustomEvent('tide-booking-payment-url', {
24037
+ detail: {
24038
+ paymentUrl: bookingResponse.paymentUrl,
24039
+ bookingNumber: bookingResponse.number
24040
+ },
24041
+ cancelable: true,
24042
+ bubbles: true
24043
+ });
24044
+ const target = typeof document !== 'undefined' ? document.getElementById('tide-booking') ?? window : window;
24045
+ const shouldAutoRedirect = target.dispatchEvent(event);
24046
+ if (shouldAutoRedirect) {
24047
+ window.location.href = bookingResponse.paymentUrl;
24048
+ }
24049
+ }
24050
+ else {
24051
+ if (settings.skipRouter) {
24052
+ dispatch(setCurrentStep$1(CONFIRMATION_STEP));
24053
+ }
24054
+ else {
24055
+ navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.confirmation.pathSuffix}?${bookingQueryString}`);
24056
+ }
24057
+ }
24058
+ }
24059
+ catch (error) {
24060
+ if (settings.skipRouter) {
24061
+ dispatch(setCurrentStep$1(ERROR_STEP));
24062
+ }
24063
+ else {
24064
+ navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.error.pathSuffix}?${bookingQueryString}`);
24065
+ }
24066
+ }
24067
+ finally {
24068
+ setIsSubmitting(false);
23539
24069
  }
23540
24070
  };
23541
- const handleAddTraveler = (roomIndex) => {
23542
- const rooms = [...formik.values.rooms];
23543
- const newAdult = {
23544
- id: Date.now(),
23545
- firstName: '',
23546
- lastName: '',
23547
- birthDate: '',
23548
- gender: '',
23549
- weight: null,
23550
- weightType: build.WeightType.kilograms,
23551
- height: null,
23552
- heightType: build.HeightType.centimeters,
23553
- customFields: buildEmptyCustomFields(paxCustomFields)
23554
- };
23555
- rooms[roomIndex] = { ...rooms[roomIndex], adults: [...rooms[roomIndex].adults, newAdult] };
23556
- formik.setFieldValue('rooms', rooms);
24071
+ const handleNotificationChange = (id, checked) => {
24072
+ const updatedNotifications = notifications.map((notification) => (notification.id === id ? { ...notification, isConfirmed: checked } : notification));
24073
+ dispatch(setNotifications(updatedNotifications));
23557
24074
  };
23558
- const handleRemoveTraveler = (roomIndex, travelerIndex) => {
23559
- const rooms = [...formik.values.rooms];
23560
- const adults = [...rooms[roomIndex].adults];
23561
- if (adults.length <= 1)
24075
+ const handleCheckboxChange = (id, checked) => {
24076
+ if (isNil(checkboxes)) {
23562
24077
  return;
23563
- adults.splice(travelerIndex, 1);
23564
- rooms[roomIndex] = { ...rooms[roomIndex], adults };
23565
- formik.setFieldValue('rooms', rooms);
23566
- };
23567
- const handleAddRoom = () => {
23568
- const rooms = [...formik.values.rooms];
23569
- rooms.push({
23570
- adults: [
23571
- {
23572
- id: Date.now(),
23573
- firstName: '',
23574
- lastName: '',
23575
- birthDate: '',
23576
- gender: '',
23577
- weight: null,
23578
- weightType: build.WeightType.kilograms,
23579
- height: null,
23580
- heightType: build.HeightType.centimeters,
23581
- customFields: buildEmptyCustomFields(paxCustomFields)
23582
- }
23583
- ],
23584
- children: []
23585
- });
23586
- formik.setFieldValue('rooms', rooms);
24078
+ }
24079
+ const newCheckboxes = [...checkboxes];
24080
+ const index = findIndex$1(checkboxes, (checkbox) => checkbox.id === id);
24081
+ newCheckboxes[index].isSelected = !newCheckboxes[index].isSelected;
24082
+ setCheckboxes(newCheckboxes);
23587
24083
  };
23588
- const handleRemoveRoom = (roomIndex) => {
23589
- const rooms = [...formik.values.rooms];
23590
- rooms.splice(roomIndex, 1);
23591
- formik.setFieldValue('rooms', rooms);
24084
+ const handleRemarksChange = (text) => {
24085
+ dispatch(setBookingRemarks(text));
24086
+ setRemarks(text);
23592
24087
  };
23593
- const renderGenderControl = (name, value) => (React__default.createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
23594
- React__default.createElement("label", { className: "form__label" },
23595
- translations.TRAVELERS_FORM.GENDER_ID,
23596
- " *"),
23597
- React__default.createElement("div", { className: "radiobutton-group" }, [
23598
- ['m', translations.TRAVELERS_FORM.MALE_GENDER],
23599
- ['f', translations.TRAVELERS_FORM.FEMALE_GENDER]
23600
- ].map(([gender, label]) => (React__default.createElement("div", { className: "radiobutton", key: gender },
23601
- React__default.createElement("label", { className: "radiobutton__label" },
23602
- React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: gender, checked: value.gender === gender }),
23603
- label)))))));
23604
- const renderWeightControl = (namePrefix, value) => (React__default.createElement("div", { className: "form__row" },
23605
- React__default.createElement(LabeledInput, { type: "number", min: 0, hasError: hasVisibleError(`${namePrefix}.weight`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.WEIGHT, name: `${namePrefix}.weight`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.weight ?? '' }),
23606
- React__default.createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.weightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.weightType, options: [
23607
- { key: 'kg', value: build.WeightType.kilograms, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_KG },
23608
- { key: 'lb', value: build.WeightType.pounds, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_LB }
23609
- ] })));
23610
- const renderHeightControl = (namePrefix, value) => (React__default.createElement("div", { className: "form__row" },
23611
- React__default.createElement(LabeledInput, { type: "number", min: 0, hasError: hasVisibleError(`${namePrefix}.height`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.HEIGHT, name: `${namePrefix}.height`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.height ?? '' }),
23612
- React__default.createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.heightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.heightType, options: [
23613
- { key: 'cm', value: build.HeightType.centimeters, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_CM },
23614
- { key: 'ftIn', value: build.HeightType.feetInches, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_FT_IN }
23615
- ] })));
23616
- const getCustomFieldControl = (field, value, name) => {
23617
- const label = compact([field.fieldName, field.isRequired && '*']).join(' ');
23618
- switch (field.dataType) {
23619
- case build.CustomFieldDataType.bool:
23620
- return (React__default.createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
23621
- React__default.createElement("div", { className: "checkbox" },
23622
- React__default.createElement("label", { className: "checkbox__label" },
23623
- 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 }),
23624
- label))));
23625
- case build.CustomFieldDataType.date:
23626
- 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 }));
23627
- case build.CustomFieldDataType.time:
23628
- 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 }));
23629
- case build.CustomFieldDataType.numeric:
23630
- 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 }));
23631
- case build.CustomFieldDataType.string:
23632
- default:
23633
- 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 }));
24088
+ const handleValidateVoucher = async () => {
24089
+ if (!voucher?.code)
24090
+ return;
24091
+ const request = {
24092
+ officeId: officeId,
24093
+ agentId: agentId,
24094
+ payload: {
24095
+ code: voucher.code,
24096
+ otherCodes: bookRequest?.payload.voucherCodes ?? []
24097
+ }
24098
+ };
24099
+ const tideClientConfig = buildTideClientConfig(apiSettings);
24100
+ if (!tideClientConfig)
24101
+ return;
24102
+ const result = await build.validateVoucher(tideClientConfig, request);
24103
+ if (result) {
24104
+ setVoucher({
24105
+ ...voucher,
24106
+ isValidated: true,
24107
+ isValid: result.isValid
24108
+ });
23634
24109
  }
23635
24110
  };
23636
- const renderCustomFields = (travelerValues, namePrefix) => {
23637
- if (!paxCustomFields?.length)
23638
- return null;
23639
- const isMainBooker = travelerValues.id === formik.values.mainBookerId;
23640
- const applicableFields = getApplicablePaxCustomFields(paxCustomFields, isMainBooker);
23641
- if (!applicableFields.length)
23642
- return null;
23643
- return (React__default.createElement("div", { className: "travelers-form__custom-fields-grid" }, applicableFields.map((field) => {
23644
- const fieldIndex = travelerValues.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
23645
- if (fieldIndex < 0)
23646
- return null;
23647
- const name = `${namePrefix}.customFields[${fieldIndex}].value`;
23648
- const value = travelerValues.customFields[fieldIndex].value;
23649
- return (React__default.createElement("div", { key: field.customFieldConfigurationId, style: { gridColumn: `span ${field.col || 12}` } }, getCustomFieldControl(field, value, name)));
23650
- })));
24111
+ const handleAddVoucher = (event) => {
24112
+ if (!voucher.isValid)
24113
+ return;
24114
+ dispatch(setVoucherCodes([...(bookRequest?.payload.voucherCodes ?? []), voucher.code ?? ''].filter((x) => x !== '')));
24115
+ dispatch(fetchPriceDetails());
24116
+ setVoucher({});
23651
24117
  };
23652
- const getControl = (type, value, name) => {
23653
- switch (type) {
23654
- case 'gender':
23655
- return React__default.createElement(GenderControl, { translations: translations, value: value, formik: formik, name: name });
23656
- case 'firstName':
23657
- 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 }));
23658
- case 'lastName':
23659
- 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 }));
23660
- case 'birthDate':
23661
- 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 }));
23662
- case 'country':
23663
- 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 }));
23664
- case 'phone':
23665
- 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 }));
23666
- case 'email':
23667
- return (React__default.createElement(React__default.Fragment, null,
23668
- 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 }),
23669
- 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 })));
23670
- case 'street':
23671
- 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 }));
23672
- case 'houseNumber':
23673
- 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 }));
23674
- case 'box':
23675
- 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 }));
23676
- case 'zipCode':
23677
- 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 }));
23678
- case 'place':
23679
- 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 }));
23680
- default:
23681
- return null;
23682
- }
24118
+ const handleRemoveVoucher = (code) => {
24119
+ dispatch(setVoucherCodes(bookRequest?.payload.voucherCodes?.filter((x) => x !== code) ?? []));
24120
+ dispatch(fetchPriceDetails());
23683
24121
  };
23684
- const renderRoomLabel = (room) => compact([
23685
- room.adults.length,
23686
- room.adults.length === 1 && ` ${translations.TRAVELERS_FORM.ADULT}`,
23687
- room.adults.length > 1 && ` ${translations.TRAVELERS_FORM.ADULTS}`,
23688
- room.adults?.length && room.children?.length && ', ',
23689
- room.children.length,
23690
- room.children.length === 1 && ` ${translations.TRAVELERS_FORM.CHILD}`,
23691
- room.children.length > 1 && ` ${translations.TRAVELERS_FORM.CHILDREN}`
23692
- ]).join('');
23693
- const renderTravelerFields = (travelerValues, namePrefix, isAdult, roomIndex, travelerIndex) => {
23694
- if (useCompactForm) {
23695
- return (React__default.createElement("div", { className: "form__row" },
23696
- 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 })));
23697
- }
23698
- if (travellersSettings?.formFields?.length) {
23699
- return (React__default.createElement(React__default.Fragment, null,
23700
- React__default.createElement("div", { className: "travelers-form__grid" }, travellersSettings.formFields.map((field, index) => (React__default.createElement("div", { key: index, className: `control control--${field.type}` }, getControl(field.type, travelerValues, `${namePrefix}.${field.type}`))))),
23701
- showWeightField && renderWeightControl(namePrefix, travelerValues),
23702
- showHeightField && renderHeightControl(namePrefix, travelerValues),
23703
- renderCustomFields(travelerValues, namePrefix)));
23704
- }
23705
- return (React__default.createElement(React__default.Fragment, null,
23706
- React__default.createElement("div", { className: "form__row" }, renderGenderControl(`${namePrefix}.gender`, travelerValues)),
23707
- React__default.createElement("div", { className: "form__row" },
23708
- React__default.createElement(LabeledInput, { hasError: hasVisibleError(`${namePrefix}.firstName`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.FIRST_NAME, required: true, name: `${namePrefix}.firstName`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.firstName }),
23709
- React__default.createElement(LabeledInput, { hasError: hasVisibleError(`${namePrefix}.lastName`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.LAST_NAME, required: true, name: `${namePrefix}.lastName`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.lastName }),
23710
- React__default.createElement(LabeledInput, { type: "date", hasError: hasVisibleError(`${namePrefix}.birthDate`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.BIRTHDATE, required: true, name: `${namePrefix}.birthDate`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.birthDate })),
23711
- showWeightField && renderWeightControl(namePrefix, travelerValues),
23712
- showHeightField && renderHeightControl(namePrefix, travelerValues),
23713
- renderCustomFields(travelerValues, namePrefix),
23714
- 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))));
24122
+ const goPrevious = () => {
24123
+ dispatch(setCurrentStep$1(travelersFirstStep ? OPTIONS_FORM_STEP : TRAVELERS_FORM_STEP));
23715
24124
  };
23716
- return (React__default.createElement("form", { className: "form form__travelers", name: "booking--travellers", id: "booking--travellers", noValidate: true, onSubmit: formik.handleSubmit, onReset: formik.handleReset },
23717
- React__default.createElement("div", { className: "form__travelers__wrapper" },
23718
- formik.values.rooms.map((room, roomIndex) => (React__default.createElement("div", { key: roomIndex },
23719
- formik.values.rooms.length > 1 && (React__default.createElement("div", { className: "form__region" },
23720
- React__default.createElement("div", { className: "form__region-header" },
23721
- React__default.createElement("h5", { className: "form__region-heading" },
23722
- translations.SHARED.ROOM,
23723
- " ",
23724
- roomIndex + 1),
23725
- React__default.createElement("p", { className: "form__region-label" }, renderRoomLabel(room))),
23726
- !useCompactForm && travelersFirstStep && formik.values.rooms.length > 1 && (React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveRoom(roomIndex) }, "Verwijder reisgezelschap")))),
23727
- room.adults.map((travelerValues, index) => (React__default.createElement("div", { className: "form__region", key: travelerValues.id },
23728
- React__default.createElement("div", { className: "form__region-header" },
23729
- React__default.createElement("h5", { className: "form__region-heading" },
23730
- translations.TRAVELERS_FORM.TRAVELER,
23731
- " ",
23732
- index + 1),
23733
- React__default.createElement("p", { className: "form__region-label" }, translations.TRAVELERS_FORM.ADULT),
23734
- React__default.createElement("div", { className: "radiobutton" },
23735
- React__default.createElement("label", { className: "radiobutton__label" },
23736
- 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" }),
23737
- translations.TRAVELERS_FORM.MAIN_BOOKER))),
23738
- renderTravelerFields(travelerValues, `rooms[${roomIndex}].adults[${index}]`, true, roomIndex, index)))),
23739
- room.children.map((travelerValues, index) => (React__default.createElement("div", { className: "form__region", key: travelerValues.id },
23740
- React__default.createElement("div", { className: "form__region-header" },
23741
- React__default.createElement("h5", { className: "form__region-heading" },
23742
- translations.TRAVELERS_FORM.TRAVELER,
23743
- " ",
23744
- room.adults.length + index + 1),
23745
- React__default.createElement("p", { className: "form__region-label" }, travelerValues.isBaby ? translations.TRAVELERS_FORM.BABY : translations.TRAVELERS_FORM.CHILD)),
23746
- renderTravelerFields(travelerValues, `rooms[${roomIndex}].children[${index}]`, false, roomIndex, index)))),
23747
- !useCompactForm && travelersFirstStep && (React__default.createElement("div", { className: "form__region" },
23748
- React__default.createElement("button", { type: "button", className: "cta cta--select", onClick: () => handleAddTraveler(roomIndex) }, translations.TRAVELERS_FORM.ADD_TRAVELER)))))),
23749
- !useCompactForm && (bookingType !== 'b2b' || travellersSettings?.mainBookerFormFields?.length) ? (React__default.createElement("div", { className: "form__region" },
23750
- React__default.createElement("div", { className: "form__region-header" },
23751
- React__default.createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.MAIN_BOOKER),
23752
- React__default.createElement("p", { className: "form__region-label" }, compact([compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '), formatBirthDate$1(mainBooker?.birthDate)]).join(', '))),
23753
- 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,
23754
- React__default.createElement("div", { className: "form__twocolumn" },
23755
- React__default.createElement("div", { className: "form__twocolumn-column" },
23756
- React__default.createElement("div", { className: "form__row" },
23757
- 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 }),
23758
- 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 }),
23759
- 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 }))),
23760
- React__default.createElement("div", { className: "form__twocolumn-column" },
23761
- React__default.createElement("div", { className: "form__row" },
23762
- 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 }),
23763
- 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 }),
23764
- 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 })))),
24125
+ const translations = useSelector(selectTranslations);
24126
+ return (React__default.createElement(React__default.Fragment, null,
24127
+ isSubmitting && (settings.loaderComponent || React__default.createElement(Loader, null)),
24128
+ !isSubmitting && (React__default.createElement("form", { className: "form", name: "booking--summary", id: "booking--summary", onSubmit: handleSubmit },
24129
+ React__default.createElement("div", { className: "form__booking--summary" },
24130
+ React__default.createElement("div", { className: "form__region" },
23765
24131
  React__default.createElement("div", { className: "form__row" },
23766
- 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 }),
23767
- 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 }),
23768
- 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" },
23769
- React__default.createElement("div", { className: "form__row" },
23770
- 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,
23771
- !useCompactForm && showAgentSelection && (React__default.createElement("div", { className: "form__region" },
23772
- React__default.createElement("div", { className: "form__region-header" },
23773
- React__default.createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.BOOK_WITH_AGENT),
23774
- React__default.createElement("div", { className: "checkbox", id: "cbxChooseOffice" },
23775
- React__default.createElement("label", { className: "checkbox__label" },
23776
- React__default.createElement("input", { type: "checkbox", name: "booking--mainbooker", checked: showAgents, onChange: () => toggleAgent(!showAgents), className: "checkbox__input" }),
23777
- translations.TRAVELERS_FORM.CHOOSE_OFFICE))),
23778
- showAgents && (React__default.createElement("div", { className: "form__row form__row--choose-office" },
23779
- React__default.createElement("div", { className: buildClassName(['form__group', 'form__group--icon', hasVisibleError('travelAgentId') && 'form__group--error']) },
23780
- 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 }))))))),
23781
- !useCompactForm && errorKeys.length > 0 && (React__default.createElement("div", { className: "form__region form__region--errors" },
23782
- React__default.createElement("div", { className: "form__row" },
23783
- React__default.createElement("div", { className: "form__group" },
23784
- React__default.createElement("p", { className: "form__error-heading" },
23785
- translations.TRAVELERS_FORM.VALIDATION_MESSAGE,
23786
- ":"),
23787
- React__default.createElement("ul", { className: "list" }, errorKeys.map((key) => (React__default.createElement("li", { key: key }, get(flatErrors, key))))))))),
23788
- travelersFirstStep && (React__default.createElement("div", { className: "booking__navigator" },
23789
- React__default.createElement("button", { type: "button", className: "cta cta--select", onClick: handleAddRoom }, translations.TRAVELERS_FORM.ADD_ROOM))),
23790
- React__default.createElement("div", { className: "booking__navigator" },
23791
- renderPreviousButton?.(),
23792
- React__default.createElement("button", { type: "submit", title: translations.STEPS.NEXT, className: 'cta' + (isUnavailable ? ' cta--disabled' : '') }, translations.STEPS.NEXT))));
24132
+ React__default.createElement("div", { className: "form__group" },
24133
+ React__default.createElement("div", { className: "form__region-header" },
24134
+ React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.PERSONAL_DETAILS)))),
24135
+ rooms.map((r, rIndex) => (React__default.createElement("div", { className: "form__row", key: rIndex },
24136
+ React__default.createElement("div", { className: "form__group" },
24137
+ React__default.createElement("div", { className: "form__region-header" },
24138
+ React__default.createElement("h5", { className: "form__region-heading" }, rooms.length > 1 ? `${translations.SHARED.ROOM} ${rIndex + 1}` : translations.ROOM_OPTIONS_FORM.TRAVELER_GROUP),
24139
+ React__default.createElement("p", { className: "form__region-label" }, `${r.adults.length + r.children.length} ${r.adults.length + r.children.length === 1 ? translations.SUMMARY.TRAVELER : translations.SUMMARY.TRAVELERS}: ${compact([
24140
+ r.adults.length,
24141
+ r.adults.length === 1 && ` ${translations.SUMMARY.ADULT}`,
24142
+ r.adults.length > 1 && ` ${translations.SUMMARY.ADULTS}`,
24143
+ r.adults && r.adults.length && r.children && r.children.length && ', ',
24144
+ r.children.length,
24145
+ r.children.length === 1 && ` ${translations.SUMMARY.CHILD}`,
24146
+ r.children.length > 1 && ` ${translations.SUMMARY.CHILDREN}`
24147
+ ]).join('')}`))),
24148
+ [...r.adults, ...r.children].map((traveler) => {
24149
+ const isMainBooker = traveler.id === travelerFormValues?.mainBookerId;
24150
+ return (React__default.createElement("div", { className: "form__group form__group--sm-50", key: traveler.id },
24151
+ React__default.createElement("ul", { className: "list list--plain" },
24152
+ React__default.createElement("li", { className: "list__item" },
24153
+ React__default.createElement("strong", null,
24154
+ traveler.firstName,
24155
+ " ",
24156
+ traveler.lastName),
24157
+ ' ',
24158
+ isMainBooker && React__default.createElement("em", null,
24159
+ "(",
24160
+ translations.SUMMARY.MAIN_BOOKER,
24161
+ ")")),
24162
+ React__default.createElement("li", { className: "list__item" }, traveler.birthDate.split('-').reverse().join('/')),
24163
+ isMainBooker && (React__default.createElement(React__default.Fragment, null,
24164
+ travelerFormValues?.street && (React__default.createElement(React__default.Fragment, null,
24165
+ React__default.createElement("li", { className: "list__item" }, `${travelerFormValues?.street} ${compact([
24166
+ travelerFormValues?.houseNumber,
24167
+ travelerFormValues?.box
24168
+ ]).join(' ')}, ${travelerFormValues?.zipCode} ${travelerFormValues?.place}`))),
24169
+ travelerFormValues?.phone && React__default.createElement("li", { className: "list__item" }, travelerFormValues?.phone),
24170
+ travelerFormValues?.email && React__default.createElement("li", { className: "list__item" }, travelerFormValues?.email))))));
24171
+ }))))),
24172
+ React__default.createElement("div", { className: "form__region" },
24173
+ React__default.createElement("div", { className: "form__row" },
24174
+ React__default.createElement("div", { className: "form__group" },
24175
+ React__default.createElement("div", { className: "form__region-header" },
24176
+ React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.OPTIONS)))),
24177
+ React__default.createElement("div", { className: "form__row" },
24178
+ React__default.createElement("div", { className: "form__group" },
24179
+ React__default.createElement("ul", { className: "list list--booking-summary" }, activeOption?.serviceType == build.ServiceType.flight ? (React__default.createElement(React__default.Fragment, null,
24180
+ departureFlightMetaData && React__default.createElement(SummaryFlight, { flightMetaData: departureFlightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }),
24181
+ returnFlightMetaData && React__default.createElement(SummaryFlight, { flightMetaData: returnFlightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT }))) : (React__default.createElement(React__default.Fragment, null, activeOption?.rooms
24182
+ .flatMap((r) => r.options)
24183
+ .filter((x) => x.isSelected)
24184
+ .map((roomOption, roomOptionIndex) => (React__default.createElement(React__default.Fragment, { key: roomOptionIndex },
24185
+ React__default.createElement("li", null,
24186
+ React__default.createElement("h6", null, roomOption?.productName),
24187
+ React__default.createElement("ul", null,
24188
+ React__default.createElement("li", { className: "list__item" },
24189
+ roomOption?.accommodationName,
24190
+ !isNil(roomOption?.regimeName) && ', ',
24191
+ roomOption?.regimeName)),
24192
+ React__default.createElement("p", null,
24193
+ "(",
24194
+ roomOption?.from === roomOption?.to ? (getDateText(roomOption?.from)) : (React__default.createElement(React__default.Fragment, null,
24195
+ getDateText(roomOption?.from),
24196
+ " > ",
24197
+ getDateText(roomOption?.to))),
24198
+ ")")),
24199
+ !isEmpty$1(activeOption?.groups) &&
24200
+ activeOption?.groups.map((x, i) => {
24201
+ if (!x.options.some((y) => y.isSelected))
24202
+ return;
24203
+ return React__default.createElement(SummaryPerBookingOptionGroup, { key: i, group: x });
24204
+ }),
24205
+ !isEmpty$1(activeOption?.optionUnits) && activeOption?.optionUnits.map((x, i) => React__default.createElement(SummaryBookingOptionUnit, { key: i, unit: x })),
24206
+ !isEmpty$1(activeOption?.optionPax) && activeOption?.optionPax.map((x, i) => React__default.createElement(SummaryBookingOptionPax, { key: i, pax: x })),
24207
+ packageDetails?.outwardFlights &&
24208
+ packageDetails.outwardFlights
24209
+ .filter((x) => x.isSelected)
24210
+ .map((flight, i) => (React__default.createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }))),
24211
+ packageDetails?.returnFlights &&
24212
+ packageDetails.returnFlights
24213
+ .filter((x) => x.isSelected)
24214
+ .map((flight, i) => (React__default.createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT })))))))))))),
24215
+ settings.enableVoucher && (React__default.createElement("div", { className: "form__region" },
24216
+ React__default.createElement("div", { className: "form__row" },
24217
+ React__default.createElement("div", { className: "form__group" },
24218
+ React__default.createElement("div", { className: "form__region-header" },
24219
+ React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.VOUCHERS)))),
24220
+ React__default.createElement("div", { className: "form__row" },
24221
+ React__default.createElement("div", { className: "form__group" },
24222
+ React__default.createElement("input", { type: "text", className: "form__input info-message__voucher__input", defaultValue: voucher.code, onChange: (e) => setVoucher({ code: e.target.value }) }),
24223
+ React__default.createElement("button", { type: "button", className: buildClassName(['cta', !voucher.code && 'cta--disabled']), onClick: (e) => handleValidateVoucher() }, translations.SUMMARY.VOUCHER_VALIDATE))),
24224
+ React__default.createElement("div", { className: "form__row" },
24225
+ React__default.createElement("div", { className: "form__group info-message__voucher" },
24226
+ voucher.isValid && voucher.isValidated && (React__default.createElement("div", { className: "info-message info-message__voucher__valid" },
24227
+ React__default.createElement("span", null, translations.SUMMARY.VOUCHER_VALID),
24228
+ React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: handleAddVoucher }, translations.SUMMARY.ADD_VOUCHER))),
24229
+ !voucher.isValid && voucher.isValidated && React__default.createElement("div", { className: "info-message--error" }, translations.SUMMARY.VOUCHER_INVALID))),
24230
+ React__default.createElement("div", { className: "form__row" },
24231
+ React__default.createElement("ul", { className: "info-message__voucher" }, !isEmpty$1(bookRequest?.payload.voucherCodes) &&
24232
+ bookRequest?.payload.voucherCodes?.map((y, i) => (React__default.createElement("li", { key: i },
24233
+ React__default.createElement("div", { className: "info-message__voucher__list" },
24234
+ y,
24235
+ ' ',
24236
+ React__default.createElement("button", { type: "button", className: "cta--add-remove", onClick: (e) => handleRemoveVoucher(y) },
24237
+ React__default.createElement(Icon, { height: 16, name: "ui-trashcan" })))))))))),
24238
+ !isEmpty$1(notifications) && (React__default.createElement("div", { className: "form__region" },
24239
+ React__default.createElement("div", { className: "form__row" },
24240
+ React__default.createElement("div", { className: "form__group" },
24241
+ React__default.createElement("div", { className: "info-message" },
24242
+ React__default.createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
24243
+ React__default.createElement("div", { className: "info-message__copy" },
24244
+ React__default.createElement("h5", null, translations.SUMMARY.NOTIFICATIONS_TITLE),
24245
+ React__default.createElement(React__default.Fragment, null,
24246
+ uniqBy(notifications.filter((x) => !x.hasToBeConfirmed), 'id').map((notification) => (React__default.createElement("span", { key: notification.id, className: "checkbox__label-text" },
24247
+ React__default.createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
24248
+ React__default.createElement("span", { className: "checkbox__label-text--description" }, notification.description)))),
24249
+ uniqBy(notifications.filter((x) => x.hasToBeConfirmed), 'id').map((notification) => (React__default.createElement("div", { className: "checkbox", key: notification.id },
24250
+ React__default.createElement("label", { className: "checkbox__label" },
24251
+ React__default.createElement("input", { type: "checkbox", className: "checkbox__input", checked: notification.isConfirmed, onChange: (e) => handleNotificationChange(notification.id, e.target.checked) }),
24252
+ React__default.createElement("span", { className: "checkbox__label-text" },
24253
+ React__default.createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
24254
+ React__default.createElement("span", { className: "checkbox__label-text--description" }, notification.description))))))))))))),
24255
+ React__default.createElement("div", { className: "form__region" },
24256
+ React__default.createElement("div", { className: "form__row" },
24257
+ React__default.createElement("div", { className: "form__group" },
24258
+ React__default.createElement("div", { className: "form__region-header" },
24259
+ React__default.createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.REMARKS)))),
24260
+ React__default.createElement("div", { className: "form__row" },
24261
+ React__default.createElement("div", { className: "form__group" },
24262
+ React__default.createElement("textarea", { className: "form__input", defaultValue: remarks, onChange: (e) => handleRemarksChange(e.target.value) })))),
24263
+ React__default.createElement("div", { className: "form__region" },
24264
+ React__default.createElement("div", { className: "form__row" },
24265
+ React__default.createElement("div", { className: "form__group" },
24266
+ React__default.createElement("div", { className: "info-message" },
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))))));
23793
24286
  };
23794
24287
 
23795
24288
  function isValidEmail(email) {
@@ -23813,7 +24306,7 @@ function getAge(birthDateText, startDateText) {
23813
24306
  }
23814
24307
  return age;
23815
24308
  }
23816
- const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE, paxCustomFields = []) => {
24309
+ const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE, paxFields = []) => {
23817
24310
  const errors = {};
23818
24311
  const isFormFieldPresent = (type) => {
23819
24312
  return formFields?.some((f) => f.type === type) ?? true;
@@ -23851,7 +24344,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23851
24344
  };
23852
24345
  const validateRequiredCustomFields = (traveler, path, room, pax) => {
23853
24346
  const isMainBooker = traveler.id === values.mainBookerId;
23854
- const requiredFields = getApplicablePaxCustomFields(paxCustomFields, isMainBooker).filter((f) => f.isRequired);
24347
+ const requiredFields = getApplicablePaxCustomFields(paxFields, isMainBooker).filter((f) => f.isRequired);
23855
24348
  requiredFields.forEach((field) => {
23856
24349
  const fieldIndex = traveler.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
23857
24350
  if (fieldIndex < 0)
@@ -23861,6 +24354,19 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23861
24354
  }
23862
24355
  });
23863
24356
  };
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
+ });
24369
+ };
23864
24370
  values.rooms.forEach((r, rIndex) => r.adults.forEach((adult, index) => {
23865
24371
  if (isEmpty$1(adult.gender) && isFormFieldPresent('gender')) {
23866
24372
  set(errors, `rooms[${rIndex}].adults[${index}].gender`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.GENDER));
@@ -23894,6 +24400,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23894
24400
  }
23895
24401
  }
23896
24402
  validateRequiredCustomFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
24403
+ validateRequiredBuiltInFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
23897
24404
  }));
23898
24405
  values.rooms.forEach((r, rIndex) => r.children.forEach((child, index) => {
23899
24406
  if (isEmpty$1(child.gender) && isFormFieldPresent('gender')) {
@@ -23936,6 +24443,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23936
24443
  }
23937
24444
  }
23938
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);
23939
24447
  }));
23940
24448
  if (values.mainBookerId < 0) {
23941
24449
  errors.mainBookerId = translations.TRAVELERS_FORM.VALIDATION.NO_MAIN_BOOKER_SELECTED;
@@ -24003,15 +24511,13 @@ const TravelersForm = () => {
24003
24511
  const bookingAttributes = useSelector(selectBookingAttributes);
24004
24512
  const hasMounted = useSelector(selectHasMounted);
24005
24513
  const countries = useSelector(selectCountries);
24006
- const paxCustomFields = useSelector(selectPaxCustomFields);
24514
+ const paxFields = useSelector(selectPaxFields);
24007
24515
  const inFlight = useRef(false);
24008
24516
  const pendingRefetch = useRef(false);
24009
24517
  const useCompactForm = !!settings.travellers.compactForm && !!settings.agentAdressId;
24010
24518
  const showAllCountries = !!settings.travellers.showAllCountries;
24011
- const showWeightField = !!settings.travellers.showWeightField;
24012
- const showHeightField = !!settings.travellers.showHeightField;
24013
24519
  const initialValues = useSelector(selectTravelersFormValues) ??
24014
- createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge, paxCustomFields);
24520
+ createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge, paxFields);
24015
24521
  const [showAgentSelection, setShowAgentSelection] = useState(!settings.agentAdressId && !settings.hideAgentSelection);
24016
24522
  const formik = useFormik({
24017
24523
  initialValues,
@@ -24021,7 +24527,7 @@ const TravelersForm = () => {
24021
24527
  // The travel dates are captured into initialValues once and then persisted in
24022
24528
  // redux, so validating against the values would use the dates of whichever
24023
24529
  // package option was selected first. Age checks need the current ones.
24024
- { ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge, paxCustomFields),
24530
+ { ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge, paxFields),
24025
24531
  onSubmit: (values) => {
24026
24532
  dispatch(setFormValues(values));
24027
24533
  if (useCompactForm) {
@@ -24104,7 +24610,7 @@ const TravelersForm = () => {
24104
24610
  dispatch(setFormValues(formValues));
24105
24611
  setShowAgentSelection(false);
24106
24612
  }, [agents, settings.affiliateSlug]);
24107
- 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, showWeightField: showWeightField, showHeightField: showHeightField, paxCustomFields: paxCustomFields, 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 }));
24108
24614
  };
24109
24615
 
24110
24616
  const Booking = ({ productCode, productName, thumbnailUrl }) => {
@@ -28251,6 +28757,22 @@ const getPackagingRequestRoomsFromBookingRooms = (rooms) => {
28251
28757
  });
28252
28758
  };
28253
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
+ };
28254
28776
  const getFlightLines = (flight) => {
28255
28777
  return flight?.flightInformation?.flightLines ?? [];
28256
28778
  };
@@ -28705,6 +29227,8 @@ const applyResultToLine = (line, result, roomIndex = 0) => {
28705
29227
  isChanged: true
28706
29228
  };
28707
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);
28708
29232
  const toDateOnlyUtcString = (value) => {
28709
29233
  const date = new Date(value);
28710
29234
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
@@ -28762,11 +29286,21 @@ const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
28762
29286
  * mapper gave them. This swaps in the guid of whatever each line resolved onto, leaving the
28763
29287
  * lines the traveller added themselves alone — those were built from a search result and already
28764
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.
28765
29294
  */
28766
29295
  const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightAvailability) => {
28767
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;
28768
29300
  getTrajectNodesWithLines(trajectEntry).forEach((item) => {
28769
29301
  if (isTrajectFlightNode(item.node)) {
29302
+ if (keepsOwnGuid(item.line.guid))
29303
+ return;
28770
29304
  // A flight search caches the outward line under the guid it reports as outwardGuid.
28771
29305
  const guid = flightAvailability[item.line.guid]?.selectedGuid;
28772
29306
  if (guid)
@@ -28779,6 +29313,8 @@ const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightA
28779
29313
  return;
28780
29314
  // Room i took option i when the result was stamped on, so it takes option i here too.
28781
29315
  item.lines.forEach((line, index) => {
29316
+ if (keepsOwnGuid(line.guid))
29317
+ return;
28782
29318
  const guid = getOptionForRoom(result, index)?.guid;
28783
29319
  if (guid)
28784
29320
  cachedGuids.set(line.guid, guid);
@@ -28842,7 +29378,7 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
28842
29378
  return { node, lines };
28843
29379
  });
28844
29380
  const claimed = new Set(nodes.flatMap((item) => item.lines.map((line) => line.guid)));
28845
- const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid));
29381
+ const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid)).filter(isMeaningfulItineraryLine);
28846
29382
  if (!extras.length)
28847
29383
  return nodes;
28848
29384
  const dayOrderOf = (date) => trajectEntry.traject.days.find((day) => isSameDay(new Date(day.date), new Date(date)))?.order ??
@@ -29019,6 +29555,195 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
29019
29555
  })));
29020
29556
  };
29021
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
+
29022
29747
  const getSegmentIcon = (segment) => {
29023
29748
  switch (segment.serviceType) {
29024
29749
  case 3:
@@ -29058,14 +29783,14 @@ const SERVICE_TYPE_PRIORITY = {
29058
29783
  const getServiceTypePriority = (serviceType) => {
29059
29784
  return SERVICE_TYPE_PRIORITY[serviceType ?? -1] ?? 2;
29060
29785
  };
29061
- const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation }) => {
29786
+ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation, onSaveEntry }) => {
29062
29787
  const context = useContext(SearchResultsConfigurationContext);
29063
29788
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
29064
29789
  const { editablePackagingEntry, priceDetails, trajectItineraryView } = useSelector((state) => state.searchResults);
29065
29790
  const dispatch = useDispatch();
29066
29791
  const packagingEntry = editablePackagingEntry ?? context?.packagingEntry;
29067
29792
  const sortedLines = useMemo(() => {
29068
- return [...(packagingEntry?.lines ?? [])].sort((a, b) => {
29793
+ return (packagingEntry?.lines ?? []).filter(isMeaningfulItineraryLine).sort((a, b) => {
29069
29794
  const dateA = getDateOnlyTime(a.from);
29070
29795
  const dateB = getDateOnlyTime(b.from);
29071
29796
  if (dateA !== dateB) {
@@ -29136,7 +29861,7 @@ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation })
29136
29861
  " ",
29137
29862
  translations.SRP.TOTAL,
29138
29863
  ")"))),
29139
- 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)),
29140
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))))),
29141
29866
  React__default.createElement("div", { className: "search__filter__itinerary" },
29142
29867
  React__default.createElement("p", null, translations.SRP.DAY_BY_DAY),
@@ -31193,7 +31918,7 @@ const selectSelectedCombinationFlight = createSelector([selectPackagingFlightRes
31193
31918
  return (packagingFlightResults.find((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey && getFlightKey(flight.return.segments) === selectedReturnKey) ?? null);
31194
31919
  });
31195
31920
 
31196
- const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price }) => {
31921
+ const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price, selectLabel, selectedLabel }) => {
31197
31922
  const context = useContext(SearchResultsConfigurationContext);
31198
31923
  const language = context?.languageCode ?? 'en-GB';
31199
31924
  const translations = getTranslations(language);
@@ -31218,7 +31943,7 @@ const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward
31218
31943
  priceDifference > 0 ? '+' : '-',
31219
31944
  "\u00A0",
31220
31945
  formatPrice$3(Math.abs(priceDifference), 'EUR', context?.languageCode ?? 'en-GB'))))),
31221
- React__default.createElement("button", { type: "button", className: `cta ${selectedGuid === guid ? 'cta--selected' : 'cta--select'}`, onClick: () => onSelect?.() }, selectedGuid === guid ? 'Selected' : 'Select')))),
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)))),
31222
31947
  React__default.createElement("div", { className: "flight__flight" },
31223
31948
  React__default.createElement("div", { className: "flight__flight__container" },
31224
31949
  React__default.createElement("div", { className: "flight__flight__wrapper" },
@@ -31852,7 +32577,7 @@ const TrajectNodeCard = ({ node, result, isSelected, languageCode, translations,
31852
32577
  const image = candidate?.imageUrl ?? null;
31853
32578
  const description = toPlainText(candidate?.description);
31854
32579
  const title = candidate?.name ?? result.name;
31855
- 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" },
31856
32581
  React__default.createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
31857
32582
  React__default.createElement("span", { className: "search__result-card__price" }, price)));
31858
32583
  const canEditOptions = isSelected && !!onEditOptions && (result.rooms ?? []).some((room) => (room.options ?? []).length > 1);
@@ -31905,6 +32630,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31905
32630
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
31906
32631
  const { trajectNodeAvailability, trajectEditLineGuid } = useSelector((state) => state.searchResults);
31907
32632
  const trajectEntry = context?.trajectEntry;
32633
+ const bookedMode = isBookedEntryTraject(trajectEntry);
31908
32634
  const item = useMemo(() => {
31909
32635
  if (!trajectEntry || !trajectEditLineGuid)
31910
32636
  return null;
@@ -31924,7 +32650,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31924
32650
  if (!result)
31925
32651
  return;
31926
32652
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
31927
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
32653
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
31928
32654
  dispatch(setTrajectEditLineGuid(null));
31929
32655
  dispatch(setFlyInIsOpen(false));
31930
32656
  };
@@ -31937,7 +32663,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31937
32663
  ordered.length,
31938
32664
  "\u00A0",
31939
32665
  translations.SRP.TOTAL_RESULTS_LABEL)),
31940
- 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 })))))));
31941
32667
  };
31942
32668
 
31943
32669
  /**
@@ -31999,6 +32725,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
31999
32725
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
32000
32726
  const { trajectNodeAvailability, trajectDaySuggestions, trajectActivitiesDayOrder, editablePackagingEntry } = useSelector((state) => state.searchResults);
32001
32727
  const trajectEntry = context?.trajectEntry;
32728
+ const bookedMode = isBookedEntryTraject(trajectEntry);
32002
32729
  const items = useMemo(() => {
32003
32730
  if (!trajectEntry || trajectActivitiesDayOrder === null)
32004
32731
  return [];
@@ -32031,7 +32758,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
32031
32758
  if (!item)
32032
32759
  return;
32033
32760
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code: result.code }));
32034
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
32761
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
32035
32762
  dispatch(setTrajectActivitiesDayOrder(null));
32036
32763
  dispatch(setFlyInIsOpen(false));
32037
32764
  };
@@ -32047,7 +32774,8 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
32047
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) => {
32048
32775
  const { result, item } = card;
32049
32776
  const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedLines(result).length > 0;
32050
- 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 ? undefined : translations.SRP.REMOVE }));
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 }));
32051
32779
  })))));
32052
32780
  };
32053
32781
 
@@ -33657,6 +34385,10 @@ const getUniqueItems = (items = []) => {
33657
34385
  return true;
33658
34386
  });
33659
34387
  };
34388
+ const isMeaningfulItem = (item, lines) => {
34389
+ const line = lines.find((l) => l.guid === item.itemGuid);
34390
+ return !line || isMeaningfulItineraryLine(line);
34391
+ };
33660
34392
  const findAccommodationForDay = (nodes, currentDay) => {
33661
34393
  for (const node of nodes ?? []) {
33662
34394
  const { startDay } = getNodeDayRange(node);
@@ -33738,7 +34470,7 @@ const buildItineraryHtml = (itinerary, translations, lines) => {
33738
34470
  const { startDay } = getNodeDayRange(node);
33739
34471
  const day = node.startDate ? format$2(node.startDate, 'd') : null;
33740
34472
  const month = node.startDate ? format$2(node.startDate, 'MMM') : null;
33741
- const uniqueItems = getUniqueItems(node.items ?? []);
34473
+ const uniqueItems = getUniqueItems(node.items ?? []).filter((item) => isMeaningfulItem(item, lines));
33742
34474
  const hasItems = uniqueItems.length > 0;
33743
34475
  const hasAccommodationInCurrentNode = uniqueItems.some(isAccommodationItem);
33744
34476
  const activeAccommodation = findAccommodationForDay(allNodes, startDay);
@@ -33782,11 +34514,12 @@ const buildItineraryHtml = (itinerary, translations, lines) => {
33782
34514
  `;
33783
34515
  })
33784
34516
  .join('');
33785
- const defaultItemsHtml = itinerary.defaultItems && itinerary.defaultItems.length > 0
34517
+ const defaultItems = getUniqueItems(itinerary.defaultItems ?? []).filter((item) => isMeaningfulItem(item, lines));
34518
+ const defaultItemsHtml = defaultItems.length > 0
33786
34519
  ? `
33787
34520
  <section class="itinerary-default-items">
33788
34521
  <h2 class="itinerary-default-items__title">Algemene info</h2>
33789
- ${getUniqueItems(itinerary.defaultItems)
34522
+ ${defaultItems
33790
34523
  .map((item) => `
33791
34524
  <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
33792
34525
  ${hasVisibleContent(item.contents)
@@ -34445,7 +35178,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
34445
35178
  const formik = useFormik({
34446
35179
  initialValues,
34447
35180
  enableReinitialize: true,
34448
- 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 ?? []),
34449
35182
  onSubmit: (values) => {
34450
35183
  if (!editablePackagingEntry)
34451
35184
  return;
@@ -34530,7 +35263,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
34530
35263
  ".\u00A0",
34531
35264
  stepLabels[step])) },
34532
35265
  isConfirmationPage && isLoading && React__default.createElement(Spinner, { label: translations.SUMMARY.PROCESS_BOOKING }),
34533
- 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 ?? [] })),
34534
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)) })),
34535
35268
  currentStep === 2 && (React__default.createElement(SharedConfirmation, { bookingNumber: bookingNumber ?? editablePackagingEntry?.dossierNumber ?? '', isOption: false, isOffer: false, translations: translations.CONFIRMATION })),
34536
35269
  currentStep === 3 && React__default.createElement("div", null))),
@@ -34594,6 +35327,7 @@ const TrajectResults = ({ isLoading }) => {
34594
35327
  const locale = getLocale(context?.languageCode ?? 'en-GB');
34595
35328
  const { trajectNodeAvailability, trajectFlightAvailability, trajectDaySuggestions, editablePackagingEntry } = useSelector((state) => state.searchResults);
34596
35329
  const trajectEntry = context?.trajectEntry;
35330
+ const bookedMode = isBookedEntryTraject(trajectEntry);
34597
35331
  const hideShowMoreResults = !!context?.hideShowMoreResults;
34598
35332
  const loadDayActivitiesOnDemand = !!context?.loadDayActivitiesOnDemand;
34599
35333
  const showNonPreferredHotels = !!context?.showNonPreferredHotels;
@@ -34650,7 +35384,7 @@ const TrajectResults = ({ isLoading }) => {
34650
35384
  if (!result)
34651
35385
  return;
34652
35386
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
34653
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
35387
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
34654
35388
  };
34655
35389
  /** The remaining legs for a flight node open in the fly-in, as the regular flight results do. */
34656
35390
  const handleShowMoreFlights = (item) => {
@@ -34738,7 +35472,7 @@ const TrajectResults = ({ isLoading }) => {
34738
35472
  node.preferredAccommodationName,
34739
35473
  " \u2014 ",
34740
35474
  translations.SRP.NO_RESULTS))),
34741
- 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 })))),
34742
35476
  remaining > 0 && !hideShowMoreResults && (React__default.createElement("div", { className: "search__results__cards__actions" },
34743
35477
  React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMore(item) },
34744
35478
  translations.SRP.SHOW_MORE,
@@ -34748,7 +35482,7 @@ const TrajectResults = ({ isLoading }) => {
34748
35482
  };
34749
35483
  const handleSelectFlight = (item, flight) => {
34750
35484
  dispatch(setTrajectFlightSelection({ lineGuid: item.line.guid, guid: flight.outwardGuid }));
34751
- dispatch(updateEditableEntryLine(applyFlightToLine(item.line, flight)));
35485
+ dispatch(updateEditableEntryLine(buildFlightSelectionLine(item, flight, bookedMode)));
34752
35486
  };
34753
35487
  const renderFlightNode = (item) => {
34754
35488
  const { node, line } = item;
@@ -34774,7 +35508,7 @@ const TrajectResults = ({ isLoading }) => {
34774
35508
  status === 'unavailable' && React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
34775
35509
  status === 'resolved' && selected && (React__default.createElement(React__default.Fragment, null,
34776
35510
  React__default.createElement("div", { className: "search__results__cards search__results__cards--extended" },
34777
- 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 }),
34778
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) })))),
34779
35513
  others.length > 2 && !hideShowMoreResults && (React__default.createElement("div", { className: "search__results__cards__actions" },
34780
35514
  React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreFlights(item) },
@@ -34858,7 +35592,8 @@ const TrajectResults = ({ isLoading }) => {
34858
35592
  */
34859
35593
  const renderActivityCard = ({ result, item }) => {
34860
35594
  const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedExcursionLines(result).length > 0;
34861
- 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 ? undefined : translations.SRP.REMOVE }));
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 }));
34862
35597
  };
34863
35598
  if (isLoading)
34864
35599
  return React__default.createElement(Spinner, { label: translations.SRP.LOADING_ITINERARY });
@@ -34902,6 +35637,7 @@ const useTrajectAvailability = () => {
34902
35637
  const controller = new AbortController();
34903
35638
  dispatch(resetTrajectAvailability());
34904
35639
  dispatch(setTrajectResolving(true));
35640
+ const bookedMode = isBookedEntryTraject(trajectEntry);
34905
35641
  const config = {
34906
35642
  host: context.tideConnection.host,
34907
35643
  apiKey: context.tideConnection.apiKey
@@ -34963,6 +35699,17 @@ const useTrajectAvailability = () => {
34963
35699
  });
34964
35700
  const configuredCodes = new Set(configuredResults.map((x) => x.code));
34965
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
+ }
34966
35713
  const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
34967
35714
  const preferredUnavailable = configuredResults.length
34968
35715
  ? !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code))
@@ -34985,6 +35732,11 @@ const useTrajectAvailability = () => {
34985
35732
  if (controller.signal.aborted)
34986
35733
  return [];
34987
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
+ }
34988
35740
  dispatch(setTrajectNodeAvailability({ lineGuid, results: [], selectedCode: null, preferredUnavailable: false }));
34989
35741
  return item.lines.map((x) => x.guid);
34990
35742
  }
@@ -34992,6 +35744,11 @@ const useTrajectAvailability = () => {
34992
35744
  const resolveFlightNode = async (item) => {
34993
35745
  const lineGuid = item.line.guid;
34994
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
+ }
34995
35752
  dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
34996
35753
  return item.lines.map((x) => x.guid);
34997
35754
  }
@@ -35000,6 +35757,11 @@ const useTrajectAvailability = () => {
35000
35757
  const flights = (await build.searchPackagingFlights(config, buildTrajectFlightRequest(item, searchContext, flightPax), controller.signal)) ?? [];
35001
35758
  if (controller.signal.aborted)
35002
35759
  return [];
35760
+ if (bookedMode) {
35761
+ const booked = buildBookedFlightResult(item.line);
35762
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked, ...flights], selectedGuid: booked.outwardGuid }));
35763
+ return [];
35764
+ }
35003
35765
  const picked = pickCheapestFlight(flights);
35004
35766
  dispatch(setTrajectFlightAvailability({ lineGuid, results: flights, selectedGuid: picked?.outwardGuid ?? null }));
35005
35767
  if (!picked)
@@ -35011,6 +35773,11 @@ const useTrajectAvailability = () => {
35011
35773
  if (controller.signal.aborted)
35012
35774
  return [];
35013
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
+ }
35014
35781
  dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
35015
35782
  return item.lines.map((x) => x.guid);
35016
35783
  }
@@ -35048,6 +35815,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35048
35815
  const [detailsIsLoading, setDetailsIsLoading] = useState(false);
35049
35816
  const [pricesAreLoading, setPricesAreLoading] = useState(false);
35050
35817
  const [itineraryIsLoading, setItineraryIsLoading] = useState(false);
35818
+ const [entrySaving, setEntrySaving] = useState(false);
35051
35819
  const [itineraryOpen, setItineraryOpen] = useState(false);
35052
35820
  const [isBookingConfirmation, setIsBookingConfirmation] = useState(false);
35053
35821
  const [selectedAccommodationSeed, setSelectedAccommodationSeed] = useState(null);
@@ -35804,6 +36572,31 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35804
36572
  useEffect(() => {
35805
36573
  setInitialFiltersSet(false);
35806
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
+ };
35807
36600
  useEffect(() => {
35808
36601
  const fetchPriceDetails = async () => {
35809
36602
  if (!context || !editablePackagingEntry || isEmpty$1(editablePackagingEntry.lines))
@@ -36188,11 +36981,18 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
36188
36981
  const id = paxId++;
36189
36982
  pax.push({
36190
36983
  id,
36984
+ guid: '',
36191
36985
  firstName: _.firstName || '',
36192
36986
  lastName: _.lastName || '',
36193
- dateOfBirth: _.dateOfBirth || null,
36194
- age: _.age || null,
36195
- 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: []
36196
36996
  });
36197
36997
  return id;
36198
36998
  });
@@ -36212,6 +37012,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
36212
37012
  depositAmount: 0,
36213
37013
  pax,
36214
37014
  rooms,
37015
+ paxFields: [],
36215
37016
  lines: [],
36216
37017
  address: {}
36217
37018
  };
@@ -36231,7 +37032,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
36231
37032
  context.searchConfiguration.qsmType != build.PortalQsmType.AccommodationAndFlight && context.showFilters && (React__default.createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => setFiltersOpen(!filtersOpen),
36232
37033
  // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
36233
37034
  isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) })),
36234
- 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 })),
36235
37036
  React__default.createElement("div", { className: "search__results" },
36236
37037
  isMobile && (React__default.createElement("div", { className: "search__result-row" },
36237
37038
  React__default.createElement("div", { className: "search__results__actions" },
@@ -36319,8 +37120,15 @@ const createSearchResultsStore = () => configureStore({
36319
37120
 
36320
37121
  const SearchResults = ({ configuration, onBookingStarted }) => {
36321
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]);
36322
37130
  return (React__default.createElement(Provider, { store: store },
36323
- React__default.createElement(SearchResultsConfigurationContext.Provider, { value: configuration },
37131
+ React__default.createElement(SearchResultsConfigurationContext.Provider, { value: effectiveConfiguration },
36324
37132
  React__default.createElement(SearchResultsContainer, { onBookingStarted: onBookingStarted }))));
36325
37133
  };
36326
37134