@qite/tide-components 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/build-cjs/index.js +1368 -395
- package/build/build-cjs/src/search-results/components/itinerary/itinerary-utils.d.ts +12 -0
- package/build/build-cjs/src/search-results/components/itinerary/traject-itinerary.d.ts +9 -0
- package/build/build-cjs/src/search-results/components/traject/traject-day-activities-flyin.d.ts +6 -0
- package/build/build-cjs/src/search-results/components/traject/traject-flights-flyin.d.ts +13 -0
- package/build/build-cjs/src/search-results/components/traject/traject-node-card.d.ts +6 -3
- package/build/build-cjs/src/search-results/hooks/use-bookable-entry.d.ts +11 -0
- package/build/build-cjs/src/search-results/hooks/use-traject-day-activities.d.ts +6 -0
- package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +14 -2
- package/build/build-cjs/src/search-results/types.d.ts +12 -2
- package/build/build-cjs/src/search-results/utils/traject-utils.d.ts +76 -2
- package/build/build-esm/index.js +1368 -395
- package/build/build-esm/src/search-results/components/itinerary/itinerary-utils.d.ts +12 -0
- package/build/build-esm/src/search-results/components/itinerary/traject-itinerary.d.ts +9 -0
- package/build/build-esm/src/search-results/components/traject/traject-day-activities-flyin.d.ts +6 -0
- package/build/build-esm/src/search-results/components/traject/traject-flights-flyin.d.ts +13 -0
- package/build/build-esm/src/search-results/components/traject/traject-node-card.d.ts +6 -3
- package/build/build-esm/src/search-results/hooks/use-bookable-entry.d.ts +11 -0
- package/build/build-esm/src/search-results/hooks/use-traject-day-activities.d.ts +6 -0
- package/build/build-esm/src/search-results/store/search-results-slice.d.ts +14 -2
- package/build/build-esm/src/search-results/types.d.ts +12 -2
- package/build/build-esm/src/search-results/utils/traject-utils.d.ts +76 -2
- package/package.json +2 -2
- package/styles/booking-search-results-variables.scss +6 -0
- package/styles/components/_search.scss +94 -5
package/build/build-cjs/index.js
CHANGED
|
@@ -438,6 +438,11 @@ var SRP$f = {
|
|
|
438
438
|
ACCOMMODATION: "الإقامة",
|
|
439
439
|
VIEW_DETAILS: "عرض التفاصيل",
|
|
440
440
|
NIGHTS: "ليالٍ",
|
|
441
|
+
OF: "من",
|
|
442
|
+
ADD: "إضافة",
|
|
443
|
+
REMOVE: "إزالة",
|
|
444
|
+
ACTIVITIES: "الأنشطة",
|
|
445
|
+
SHOW_ACTIVITIES: "عرض الأنشطة",
|
|
441
446
|
MORNING_RANGE: "الصباح",
|
|
442
447
|
AFTERNOON_RANGE: "بعد الظهر",
|
|
443
448
|
EVENING_RANGE: "المساء",
|
|
@@ -453,6 +458,7 @@ var SRP$f = {
|
|
|
453
458
|
TRAVEL_GROUP: "مجموعة المسافرين",
|
|
454
459
|
EXCURSION: "رحلة",
|
|
455
460
|
LOADING_EXCURSIONS: "جارٍ تحميل الرحلات...",
|
|
461
|
+
LOADING_ACTIVITIES: "جارٍ تحميل الأنشطة...",
|
|
456
462
|
LOADING_FLIGHTS: "جارٍ تحميل الرحلات الجوية...",
|
|
457
463
|
LOADING_ACCOMMODATIONS: "جارٍ تحميل الإقامات...",
|
|
458
464
|
LOADING_ITINERARY: "جارٍ تحميل خط سير الرحلة...",
|
|
@@ -877,6 +883,11 @@ var SRP$e = {
|
|
|
877
883
|
ACCOMMODATION: "Indkvartering",
|
|
878
884
|
VIEW_DETAILS: "Se detaljer",
|
|
879
885
|
NIGHTS: "nætter",
|
|
886
|
+
OF: "af",
|
|
887
|
+
ADD: "Tilføj",
|
|
888
|
+
REMOVE: "Fjern",
|
|
889
|
+
ACTIVITIES: "Aktiviteter",
|
|
890
|
+
SHOW_ACTIVITIES: "Vis aktiviteter",
|
|
880
891
|
MORNING_RANGE: "Morgen",
|
|
881
892
|
AFTERNOON_RANGE: "Eftermiddag",
|
|
882
893
|
EVENING_RANGE: "Aften",
|
|
@@ -892,6 +903,7 @@ var SRP$e = {
|
|
|
892
903
|
TRAVEL_GROUP: "Rejseselskab",
|
|
893
904
|
EXCURSION: "Udflugt",
|
|
894
905
|
LOADING_EXCURSIONS: "Indlæser udflugter...",
|
|
906
|
+
LOADING_ACTIVITIES: "Indlæser aktiviteter...",
|
|
895
907
|
LOADING_FLIGHTS: "Indlæser fly...",
|
|
896
908
|
LOADING_ACCOMMODATIONS: "Indlæser indkvarteringer...",
|
|
897
909
|
LOADING_ITINERARY: "Indlæser rejseplan...",
|
|
@@ -1320,6 +1332,11 @@ var SRP$d = {
|
|
|
1320
1332
|
ACCOMMODATION: "Unterkunft",
|
|
1321
1333
|
VIEW_DETAILS: "Details anzeigen",
|
|
1322
1334
|
NIGHTS: "Nächte",
|
|
1335
|
+
OF: "von",
|
|
1336
|
+
ADD: "Hinzufügen",
|
|
1337
|
+
REMOVE: "Entfernen",
|
|
1338
|
+
ACTIVITIES: "Aktivitäten",
|
|
1339
|
+
SHOW_ACTIVITIES: "Aktivitäten anzeigen",
|
|
1323
1340
|
MORNING_RANGE: "Morgen",
|
|
1324
1341
|
AFTERNOON_RANGE: "Nachmittag",
|
|
1325
1342
|
EVENING_RANGE: "Abend",
|
|
@@ -1331,6 +1348,7 @@ var SRP$d = {
|
|
|
1331
1348
|
TRAVEL_GROUP: "Reisegruppe",
|
|
1332
1349
|
EXCURSION: "Ausflug",
|
|
1333
1350
|
LOADING_EXCURSIONS: "Lade Ausflüge...",
|
|
1351
|
+
LOADING_ACTIVITIES: "Lade Aktivitäten...",
|
|
1334
1352
|
LOADING_FLIGHTS: "Lade Flüge...",
|
|
1335
1353
|
LOADING_ACCOMMODATIONS: "Lade Unterkünfte...",
|
|
1336
1354
|
LOADING_ITINERARY: "Lade Reiseverlauf...",
|
|
@@ -1763,6 +1781,11 @@ var SRP$c = {
|
|
|
1763
1781
|
ACCOMMODATION: "Accommodation",
|
|
1764
1782
|
VIEW_DETAILS: "View details",
|
|
1765
1783
|
NIGHTS: "nights",
|
|
1784
|
+
OF: "of",
|
|
1785
|
+
ADD: "Add",
|
|
1786
|
+
REMOVE: "Remove",
|
|
1787
|
+
ACTIVITIES: "Activities",
|
|
1788
|
+
SHOW_ACTIVITIES: "Show activities",
|
|
1766
1789
|
PRICE: "Price",
|
|
1767
1790
|
MORNING_RANGE: "Morning",
|
|
1768
1791
|
AFTERNOON_RANGE: "Afternoon",
|
|
@@ -1774,6 +1797,7 @@ var SRP$c = {
|
|
|
1774
1797
|
TRAVEL_GROUP: "Travel group",
|
|
1775
1798
|
EXCURSION: "Excursion",
|
|
1776
1799
|
LOADING_EXCURSIONS: "Loading excursions...",
|
|
1800
|
+
LOADING_ACTIVITIES: "Loading activities...",
|
|
1777
1801
|
LOADING_FLIGHTS: "Loading flights...",
|
|
1778
1802
|
LOADING_ACCOMMODATIONS: "Loading accommodations...",
|
|
1779
1803
|
LOADING_ITINERARY: "Loading itinerary...",
|
|
@@ -2202,6 +2226,11 @@ var SRP$b = {
|
|
|
2202
2226
|
ACCOMMODATION: "Alojamiento",
|
|
2203
2227
|
VIEW_DETAILS: "Ver detalles",
|
|
2204
2228
|
NIGHTS: "noches",
|
|
2229
|
+
OF: "de",
|
|
2230
|
+
ADD: "Añadir",
|
|
2231
|
+
REMOVE: "Eliminar",
|
|
2232
|
+
ACTIVITIES: "Actividades",
|
|
2233
|
+
SHOW_ACTIVITIES: "Ver actividades",
|
|
2205
2234
|
MORNING_RANGE: "Mañana",
|
|
2206
2235
|
AFTERNOON_RANGE: "Tarde",
|
|
2207
2236
|
EVENING_RANGE: "Noche",
|
|
@@ -2213,6 +2242,7 @@ var SRP$b = {
|
|
|
2213
2242
|
TRAVEL_GROUP: "Grupo de viaje",
|
|
2214
2243
|
EXCURSION: "Excursión",
|
|
2215
2244
|
LOADING_EXCURSIONS: "Cargando excursiones...",
|
|
2245
|
+
LOADING_ACTIVITIES: "Cargando actividades...",
|
|
2216
2246
|
LOADING_FLIGHTS: "Cargando vuelos...",
|
|
2217
2247
|
LOADING_ACCOMMODATIONS: "Cargando alojamientos...",
|
|
2218
2248
|
LOADING_ITINERARY: "Cargando itinerario...",
|
|
@@ -2645,6 +2675,11 @@ var SRP$a = {
|
|
|
2645
2675
|
ACCOMMODATION: "Hébergement",
|
|
2646
2676
|
VIEW_DETAILS: "Voir les détails",
|
|
2647
2677
|
NIGHTS: "nuits",
|
|
2678
|
+
OF: "sur",
|
|
2679
|
+
ADD: "Ajouter",
|
|
2680
|
+
REMOVE: "Supprimer",
|
|
2681
|
+
ACTIVITIES: "Activités",
|
|
2682
|
+
SHOW_ACTIVITIES: "Voir les activités",
|
|
2648
2683
|
MORNING_RANGE: "Matin",
|
|
2649
2684
|
AFTERNOON_RANGE: "Après-midi",
|
|
2650
2685
|
EVENING_RANGE: "Soir",
|
|
@@ -2656,6 +2691,7 @@ var SRP$a = {
|
|
|
2656
2691
|
TRAVEL_GROUP: "Groupe de voyageurs",
|
|
2657
2692
|
EXCURSION: "Excursion",
|
|
2658
2693
|
LOADING_EXCURSIONS: "Chargement des excursions...",
|
|
2694
|
+
LOADING_ACTIVITIES: "Chargement des activités...",
|
|
2659
2695
|
LOADING_FLIGHTS: "Chargement des vols...",
|
|
2660
2696
|
LOADING_ACCOMMODATIONS: "Chargement des hébergements...",
|
|
2661
2697
|
LOADING_ITINERARY: "Chargement de l’itinéraire...",
|
|
@@ -3084,6 +3120,11 @@ var SRP$9 = {
|
|
|
3084
3120
|
ACCOMMODATION: "Hébergement",
|
|
3085
3121
|
VIEW_DETAILS: "Voir les détails",
|
|
3086
3122
|
NIGHTS: "nuits",
|
|
3123
|
+
OF: "sur",
|
|
3124
|
+
ADD: "Ajouter",
|
|
3125
|
+
REMOVE: "Supprimer",
|
|
3126
|
+
ACTIVITIES: "Activités",
|
|
3127
|
+
SHOW_ACTIVITIES: "Voir les activités",
|
|
3087
3128
|
MORNING_RANGE: "Matin",
|
|
3088
3129
|
AFTERNOON_RANGE: "Après-midi",
|
|
3089
3130
|
EVENING_RANGE: "Soir",
|
|
@@ -3095,6 +3136,7 @@ var SRP$9 = {
|
|
|
3095
3136
|
TRAVEL_GROUP: "Groupe de voyageurs",
|
|
3096
3137
|
EXCURSION: "Excursion",
|
|
3097
3138
|
LOADING_EXCURSIONS: "Chargement des excursions...",
|
|
3139
|
+
LOADING_ACTIVITIES: "Chargement des activités...",
|
|
3098
3140
|
LOADING_FLIGHTS: "Chargement des vols...",
|
|
3099
3141
|
LOADING_ACCOMMODATIONS: "Chargement des hébergements...",
|
|
3100
3142
|
LOADING_ITINERARY: "Chargement de l’itinéraire...",
|
|
@@ -3523,6 +3565,11 @@ var SRP$8 = {
|
|
|
3523
3565
|
ACCOMMODATION: "Gisting",
|
|
3524
3566
|
VIEW_DETAILS: "Skoða upplýsingar",
|
|
3525
3567
|
NIGHTS: "nætur",
|
|
3568
|
+
OF: "af",
|
|
3569
|
+
ADD: "Bæta við",
|
|
3570
|
+
REMOVE: "Fjarlægja",
|
|
3571
|
+
ACTIVITIES: "Afþreying",
|
|
3572
|
+
SHOW_ACTIVITIES: "Sýna afþreyingu",
|
|
3526
3573
|
MORNING_RANGE: "Morgunn",
|
|
3527
3574
|
AFTERNOON_RANGE: "Síðdegi",
|
|
3528
3575
|
EVENING_RANGE: "Kvöld",
|
|
@@ -3534,6 +3581,7 @@ var SRP$8 = {
|
|
|
3534
3581
|
TRAVEL_GROUP: "Ferðahópur",
|
|
3535
3582
|
EXCURSION: "Útflutningur",
|
|
3536
3583
|
LOADING_EXCURSIONS: "Hleðst inn í útflutninga...",
|
|
3584
|
+
LOADING_ACTIVITIES: "Hleð inn afþreyingu...",
|
|
3537
3585
|
LOADING_FLIGHTS: "Hleðst inn í flug...",
|
|
3538
3586
|
LOADING_ACCOMMODATIONS: "Hleðst inn í gistingar...",
|
|
3539
3587
|
LOADING_ITINERARY: "Hleðst inn í ferðaráætlun...",
|
|
@@ -3962,6 +4010,11 @@ var SRP$7 = {
|
|
|
3962
4010
|
ACCOMMODATION: "Alloggio",
|
|
3963
4011
|
VIEW_DETAILS: "Visualizza dettagli",
|
|
3964
4012
|
NIGHTS: "notti",
|
|
4013
|
+
OF: "di",
|
|
4014
|
+
ADD: "Aggiungi",
|
|
4015
|
+
REMOVE: "Rimuovi",
|
|
4016
|
+
ACTIVITIES: "Attività",
|
|
4017
|
+
SHOW_ACTIVITIES: "Mostra attività",
|
|
3965
4018
|
MORNING_RANGE: "Mattina",
|
|
3966
4019
|
AFTERNOON_RANGE: "Pomeriggio",
|
|
3967
4020
|
EVENING_RANGE: "Sera",
|
|
@@ -3973,6 +4026,7 @@ var SRP$7 = {
|
|
|
3973
4026
|
TRAVEL_GROUP: "Gruppo di viaggio",
|
|
3974
4027
|
EXCURSION: "Escursione",
|
|
3975
4028
|
LOADING_EXCURSIONS: "Caricamento delle escursioni...",
|
|
4029
|
+
LOADING_ACTIVITIES: "Caricamento delle attività...",
|
|
3976
4030
|
LOADING_FLIGHTS: "Caricamento dei voli...",
|
|
3977
4031
|
LOADING_ACCOMMODATIONS: "Caricamento degli alloggi...",
|
|
3978
4032
|
LOADING_ITINERARY: "Caricamento dell'itinerario...",
|
|
@@ -4405,6 +4459,11 @@ var SRP$6 = {
|
|
|
4405
4459
|
ACCOMMODATION: "Accommodatie",
|
|
4406
4460
|
VIEW_DETAILS: "Bekijk details",
|
|
4407
4461
|
NIGHTS: "nachten",
|
|
4462
|
+
OF: "van",
|
|
4463
|
+
ADD: "Toevoegen",
|
|
4464
|
+
REMOVE: "Verwijderen",
|
|
4465
|
+
ACTIVITIES: "Activiteiten",
|
|
4466
|
+
SHOW_ACTIVITIES: "Toon activiteiten",
|
|
4408
4467
|
MORNING_RANGE: "Ochtend",
|
|
4409
4468
|
AFTERNOON_RANGE: "Namiddag",
|
|
4410
4469
|
EVENING_RANGE: "Avond",
|
|
@@ -4416,6 +4475,7 @@ var SRP$6 = {
|
|
|
4416
4475
|
TRAVEL_GROUP: "Reisgezelschap",
|
|
4417
4476
|
EXCURSION: "Excursie",
|
|
4418
4477
|
LOADING_EXCURSIONS: "Excursies laden...",
|
|
4478
|
+
LOADING_ACTIVITIES: "Activiteiten laden...",
|
|
4419
4479
|
LOADING_FLIGHTS: "Vluchten laden...",
|
|
4420
4480
|
LOADING_ACCOMMODATIONS: "Accommodaties laden...",
|
|
4421
4481
|
LOADING_ITINERARY: "Reisplan laden...",
|
|
@@ -4844,6 +4904,11 @@ var SRP$5 = {
|
|
|
4844
4904
|
ACCOMMODATION: "Accommodatie",
|
|
4845
4905
|
VIEW_DETAILS: "Bekijk details",
|
|
4846
4906
|
NIGHTS: "nachten",
|
|
4907
|
+
OF: "van",
|
|
4908
|
+
ADD: "Toevoegen",
|
|
4909
|
+
REMOVE: "Verwijderen",
|
|
4910
|
+
ACTIVITIES: "Activiteiten",
|
|
4911
|
+
SHOW_ACTIVITIES: "Toon activiteiten",
|
|
4847
4912
|
MORNING_RANGE: "Ochtend",
|
|
4848
4913
|
AFTERNOON_RANGE: "Namiddag",
|
|
4849
4914
|
EVENING_RANGE: "Avond",
|
|
@@ -4855,6 +4920,7 @@ var SRP$5 = {
|
|
|
4855
4920
|
TRAVEL_GROUP: "Reisgezelschap",
|
|
4856
4921
|
EXCURSION: "Excursie",
|
|
4857
4922
|
LOADING_EXCURSIONS: "Excursies laden...",
|
|
4923
|
+
LOADING_ACTIVITIES: "Activiteiten laden...",
|
|
4858
4924
|
LOADING_FLIGHTS: "Vluchten laden...",
|
|
4859
4925
|
LOADING_ACCOMMODATIONS: "Accommodaties laden...",
|
|
4860
4926
|
LOADING_ITINERARY: "Reisplan laden...",
|
|
@@ -5283,6 +5349,11 @@ var SRP$4 = {
|
|
|
5283
5349
|
ACCOMMODATION: "Overnatting",
|
|
5284
5350
|
VIEW_DETAILS: "Se detaljer",
|
|
5285
5351
|
NIGHTS: "netter",
|
|
5352
|
+
OF: "av",
|
|
5353
|
+
ADD: "Legg til",
|
|
5354
|
+
REMOVE: "Fjern",
|
|
5355
|
+
ACTIVITIES: "Aktiviteter",
|
|
5356
|
+
SHOW_ACTIVITIES: "Vis aktiviteter",
|
|
5286
5357
|
MORNING_RANGE: "Morgen",
|
|
5287
5358
|
AFTERNOON_RANGE: "Ettermiddag",
|
|
5288
5359
|
EVENING_RANGE: "Kveld",
|
|
@@ -5294,6 +5365,7 @@ var SRP$4 = {
|
|
|
5294
5365
|
TRAVEL_GROUP: "Reisefølge",
|
|
5295
5366
|
EXCURSION: "Utflukt",
|
|
5296
5367
|
LOADING_EXCURSIONS: "Laster utflukter...",
|
|
5368
|
+
LOADING_ACTIVITIES: "Laster aktiviteter...",
|
|
5297
5369
|
LOADING_FLIGHTS: "Laster flyvninger...",
|
|
5298
5370
|
LOADING_ACCOMMODATIONS: "Laster overnattinger...",
|
|
5299
5371
|
LOADING_ITINERARY: "Laster reiserute...",
|
|
@@ -5722,6 +5794,11 @@ var SRP$3 = {
|
|
|
5722
5794
|
ACCOMMODATION: "Zakwaterowanie",
|
|
5723
5795
|
VIEW_DETAILS: "Zobacz szczegóły",
|
|
5724
5796
|
NIGHTS: "noce",
|
|
5797
|
+
OF: "z",
|
|
5798
|
+
ADD: "Dodaj",
|
|
5799
|
+
REMOVE: "Usuń",
|
|
5800
|
+
ACTIVITIES: "Atrakcje",
|
|
5801
|
+
SHOW_ACTIVITIES: "Pokaż atrakcje",
|
|
5725
5802
|
MORNING_RANGE: "Poranek",
|
|
5726
5803
|
AFTERNOON_RANGE: "Popołudnie",
|
|
5727
5804
|
EVENING_RANGE: "Wieczór",
|
|
@@ -5733,6 +5810,7 @@ var SRP$3 = {
|
|
|
5733
5810
|
TRAVEL_GROUP: "Grupa podróżnych",
|
|
5734
5811
|
EXCURSION: "Wycieczka",
|
|
5735
5812
|
LOADING_EXCURSIONS: "Ładowanie wycieczek...",
|
|
5813
|
+
LOADING_ACTIVITIES: "Ładowanie atrakcji...",
|
|
5736
5814
|
LOADING_FLIGHTS: "Ładowanie lotów...",
|
|
5737
5815
|
LOADING_ACCOMMODATIONS: "Ładowanie zakwaterowań...",
|
|
5738
5816
|
LOADING_ITINERARY: "Ładowanie planu podróży...",
|
|
@@ -6161,6 +6239,11 @@ var SRP$2 = {
|
|
|
6161
6239
|
ACCOMMODATION: "Alojamento",
|
|
6162
6240
|
VIEW_DETAILS: "Ver detalhes",
|
|
6163
6241
|
NIGHTS: "noites",
|
|
6242
|
+
OF: "de",
|
|
6243
|
+
ADD: "Adicionar",
|
|
6244
|
+
REMOVE: "Remover",
|
|
6245
|
+
ACTIVITIES: "Atividades",
|
|
6246
|
+
SHOW_ACTIVITIES: "Ver atividades",
|
|
6164
6247
|
MORNING_RANGE: "Manhã",
|
|
6165
6248
|
AFTERNOON_RANGE: "Tarde",
|
|
6166
6249
|
EVENING_RANGE: "Noite",
|
|
@@ -6172,6 +6255,7 @@ var SRP$2 = {
|
|
|
6172
6255
|
TRAVEL_GROUP: "Grupo de viajantes",
|
|
6173
6256
|
EXCURSION: "Excursão",
|
|
6174
6257
|
LOADING_EXCURSIONS: "A carregar excursões...",
|
|
6258
|
+
LOADING_ACTIVITIES: "A carregar atividades...",
|
|
6175
6259
|
LOADING_FLIGHTS: "A carregar voos...",
|
|
6176
6260
|
LOADING_ACCOMMODATIONS: "A carregar alojamentos...",
|
|
6177
6261
|
LOADING_ITINERARY: "A carregar itinerário...",
|
|
@@ -6600,6 +6684,11 @@ var SRP$1 = {
|
|
|
6600
6684
|
ACCOMMODATION: "Boende",
|
|
6601
6685
|
VIEW_DETAILS: "Visa detaljer",
|
|
6602
6686
|
NIGHTS: "nätter",
|
|
6687
|
+
OF: "av",
|
|
6688
|
+
ADD: "Lägg till",
|
|
6689
|
+
REMOVE: "Ta bort",
|
|
6690
|
+
ACTIVITIES: "Aktiviteter",
|
|
6691
|
+
SHOW_ACTIVITIES: "Visa aktiviteter",
|
|
6603
6692
|
MORNING_RANGE: "Morgon",
|
|
6604
6693
|
AFTERNOON_RANGE: "Eftermiddag",
|
|
6605
6694
|
EVENING_RANGE: "Kväll",
|
|
@@ -6611,6 +6700,7 @@ var SRP$1 = {
|
|
|
6611
6700
|
TRAVEL_GROUP: "Resesällskap",
|
|
6612
6701
|
EXCURSION: "Utflykt",
|
|
6613
6702
|
LOADING_EXCURSIONS: "Laddar utflykter...",
|
|
6703
|
+
LOADING_ACTIVITIES: "Laddar aktiviteter...",
|
|
6614
6704
|
LOADING_FLIGHTS: "Laddar flyg...",
|
|
6615
6705
|
LOADING_ACCOMMODATIONS: "Laddar boenden...",
|
|
6616
6706
|
LOADING_ITINERARY: "Laddar resplan...",
|
|
@@ -7039,6 +7129,11 @@ var SRP = {
|
|
|
7039
7129
|
ACCOMMODATION: "宿泊",
|
|
7040
7130
|
VIEW_DETAILS: "詳細を見る",
|
|
7041
7131
|
NIGHTS: "泊",
|
|
7132
|
+
OF: "/",
|
|
7133
|
+
ADD: "追加",
|
|
7134
|
+
REMOVE: "削除",
|
|
7135
|
+
ACTIVITIES: "アクティビティ",
|
|
7136
|
+
SHOW_ACTIVITIES: "アクティビティを表示",
|
|
7042
7137
|
MORNING_RANGE: "朝",
|
|
7043
7138
|
AFTERNOON_RANGE: "午後",
|
|
7044
7139
|
EVENING_RANGE: "夕方",
|
|
@@ -7050,6 +7145,7 @@ var SRP = {
|
|
|
7050
7145
|
TRAVEL_GROUP: "旅行グループ",
|
|
7051
7146
|
EXCURSION: "エクスカーション",
|
|
7052
7147
|
LOADING_EXCURSIONS: "エクスカーションを読み込み中...",
|
|
7148
|
+
LOADING_ACTIVITIES: "アクティビティを読み込み中...",
|
|
7053
7149
|
LOADING_FLIGHTS: "フライトを読み込み中...",
|
|
7054
7150
|
LOADING_ACCOMMODATIONS: "宿泊施設を読み込み中...",
|
|
7055
7151
|
LOADING_ITINERARY: "旅程を読み込み中...",
|
|
@@ -16532,7 +16628,7 @@ const Icon = ({ name, className, title, width, height, fill }) => {
|
|
|
16532
16628
|
title && React__default["default"].createElement("title", null, title),
|
|
16533
16629
|
React__default["default"].createElement("path", { d: "M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z" })));
|
|
16534
16630
|
case 'ui-hotel':
|
|
16535
|
-
return (React__default["default"].createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !lodash.isEmpty(className)).join(' '), width: width, height: height, viewBox: "0 0
|
|
16631
|
+
return (React__default["default"].createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !lodash.isEmpty(className)).join(' '), width: width, height: height, viewBox: "0 0 512 512", fill: fill ?? 'currentColor' },
|
|
16536
16632
|
React__default["default"].createElement(HTMLComment, { text: "!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc." }),
|
|
16537
16633
|
title && React__default["default"].createElement("title", null, title),
|
|
16538
16634
|
React__default["default"].createElement("path", { d: "M16 24C16 10.7 26.7 0 40 0L472 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-8 0 0 416 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24L40 512c-13.3 0-24-10.7-24-24s10.7-24 24-24l8 0 0-416-8 0C26.7 48 16 37.3 16 24zm208 88l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zM128 96c-8.8 0-16 7.2-16 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0zm96 112l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zM352 96c-8.8 0-16 7.2-16 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0zM112 208l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm240-16c-8.8 0-16 7.2-16 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0zM288 384l43.8 0c9.9 0 17.5-9 14-18.2-13.8-36.1-48.8-61.8-89.7-61.8s-75.9 25.7-89.7 61.8c-3.5 9.2 4.1 18.2 14 18.2l43.8 0 0 80 64 0 0-80z", fill: "currentColor" })));
|
|
@@ -16561,6 +16657,16 @@ const Icon = ({ name, className, title, width, height, fill }) => {
|
|
|
16561
16657
|
React__default["default"].createElement(HTMLComment, { text: "!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc." }),
|
|
16562
16658
|
title && React__default["default"].createElement("title", null, title),
|
|
16563
16659
|
React__default["default"].createElement("path", { d: "M135.2 117.4l-26.1 74.6 293.8 0-26.1-74.6C372.3 104.6 360.2 96 346.6 96L165.4 96c-13.6 0-25.7 8.6-30.2 21.4zM39.6 196.8L74.8 96.3C88.3 57.8 124.6 32 165.4 32l181.2 0c40.8 0 77.1 25.8 90.6 64.3l35.2 100.5c23.2 9.6 39.6 32.5 39.6 59.2l0 192c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32l0-32-320 0 0 32c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32L0 256c0-26.7 16.4-49.6 39.6-59.2zM128 304a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm288 32a32 32 0 1 0 0-64 32 32 0 1 0 0 64z", fill: "currentColor" })));
|
|
16660
|
+
case 'ui-bus':
|
|
16661
|
+
return (React__default["default"].createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !lodash.isEmpty(className)).join(' '), width: width, height: height, viewBox: "0 0 512 512", fill: fill ?? 'currentColor' },
|
|
16662
|
+
title && React__default["default"].createElement("title", null, title),
|
|
16663
|
+
React__default["default"].createElement("path", { d: "M112 32h288a64 64 0 0 1 64 64v256a64 64 0 0 1-64 64h-288a64 64 0 0 1-64-64V96a64 64 0 0 1 64-64zM96 112v104h136V112H96zm184 0v104h136V112H280zM160 352a36 36 0 1 0-72 0 36 36 0 1 0 72 0zm264 0a36 36 0 1 0-72 0 36 36 0 1 0 72 0z", fill: "currentColor", fillRule: "evenodd" }),
|
|
16664
|
+
React__default["default"].createElement("path", { d: "M112 416h64v48a24 24 0 0 1-24 24h-16a24 24 0 0 1-24-24v-48zm224 0h64v48a24 24 0 0 1-24 24h-16a24 24 0 0 1-24-24v-48z", fill: "currentColor" })));
|
|
16665
|
+
case 'ui-train':
|
|
16666
|
+
return (React__default["default"].createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !lodash.isEmpty(className)).join(' '), width: width, height: height, viewBox: "0 0 512 512", fill: fill ?? 'currentColor' },
|
|
16667
|
+
title && React__default["default"].createElement("title", null, title),
|
|
16668
|
+
React__default["default"].createElement("path", { d: "M160 32h192a64 64 0 0 1 64 64v224a64 64 0 0 1-64 64h-192a64 64 0 0 1-64-64V96a64 64 0 0 1 64-64zm-24 80v104h240V112H136zm56 168a28 28 0 1 0-56 0 28 28 0 1 0 56 0zm192 0a28 28 0 1 0-56 0 28 28 0 1 0 56 0z", fill: "currentColor", fillRule: "evenodd" }),
|
|
16669
|
+
React__default["default"].createElement("path", { d: "M168 416h72l-48 64h-72l48-64zm104 0h72l48 64h-72l-48-64z", fill: "currentColor" })));
|
|
16564
16670
|
case 'ui-ship':
|
|
16565
16671
|
return (React__default["default"].createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !lodash.isEmpty(className)).join(' '), width: width, height: height, viewBox: "0 0 640 512", fill: fill ?? 'currentColor' },
|
|
16566
16672
|
React__default["default"].createElement(HTMLComment, { text: "!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc." }),
|
|
@@ -27379,7 +27485,11 @@ const initialState$1 = {
|
|
|
27379
27485
|
bookingNumber: undefined,
|
|
27380
27486
|
trajectNodeAvailability: {},
|
|
27381
27487
|
trajectFlightAvailability: {},
|
|
27382
|
-
|
|
27488
|
+
trajectDaySuggestions: {},
|
|
27489
|
+
trajectEditLineGuid: null,
|
|
27490
|
+
trajectActivitiesDayOrder: null,
|
|
27491
|
+
trajectResolving: false,
|
|
27492
|
+
trajectItineraryView: false
|
|
27383
27493
|
};
|
|
27384
27494
|
const searchResultsSlice = toolkit.createSlice({
|
|
27385
27495
|
name: 'searchResults',
|
|
@@ -27595,6 +27705,11 @@ const searchResultsSlice = toolkit.createSlice({
|
|
|
27595
27705
|
return;
|
|
27596
27706
|
state.editablePackagingEntry.lines[index] = action.payload;
|
|
27597
27707
|
},
|
|
27708
|
+
addEditableEntryLines(state, action) {
|
|
27709
|
+
if (!state.editablePackagingEntry)
|
|
27710
|
+
return;
|
|
27711
|
+
state.editablePackagingEntry.lines = [...(state.editablePackagingEntry.lines ?? []), ...action.payload];
|
|
27712
|
+
},
|
|
27598
27713
|
removeEditableEntryLines(state, action) {
|
|
27599
27714
|
if (!state.editablePackagingEntry)
|
|
27600
27715
|
return;
|
|
@@ -27626,9 +27741,30 @@ const searchResultsSlice = toolkit.createSlice({
|
|
|
27626
27741
|
flight.selectedGuid = action.payload.guid;
|
|
27627
27742
|
flight.status = 'resolved';
|
|
27628
27743
|
},
|
|
27744
|
+
setTrajectDaySuggestionsLoading(state, action) {
|
|
27745
|
+
const dayOrder = action.payload;
|
|
27746
|
+
state.trajectDaySuggestions[dayOrder] = {
|
|
27747
|
+
dayOrder,
|
|
27748
|
+
status: 'loading',
|
|
27749
|
+
results: state.trajectDaySuggestions[dayOrder]?.results ?? []
|
|
27750
|
+
};
|
|
27751
|
+
},
|
|
27752
|
+
setTrajectDaySuggestions(state, action) {
|
|
27753
|
+
const { dayOrder, results } = action.payload;
|
|
27754
|
+
state.trajectDaySuggestions[dayOrder] = { dayOrder, status: 'resolved', results };
|
|
27755
|
+
},
|
|
27629
27756
|
setTrajectEditLineGuid(state, action) {
|
|
27630
27757
|
state.trajectEditLineGuid = action.payload;
|
|
27631
27758
|
},
|
|
27759
|
+
setTrajectActivitiesDayOrder(state, action) {
|
|
27760
|
+
state.trajectActivitiesDayOrder = action.payload;
|
|
27761
|
+
},
|
|
27762
|
+
setTrajectResolving(state, action) {
|
|
27763
|
+
state.trajectResolving = action.payload;
|
|
27764
|
+
},
|
|
27765
|
+
setTrajectItineraryView(state, action) {
|
|
27766
|
+
state.trajectItineraryView = action.payload;
|
|
27767
|
+
},
|
|
27632
27768
|
updateTrajectNodeResult(state, action) {
|
|
27633
27769
|
const { lineGuid, result } = action.payload;
|
|
27634
27770
|
const node = state.trajectNodeAvailability[lineGuid];
|
|
@@ -27641,11 +27777,15 @@ const searchResultsSlice = toolkit.createSlice({
|
|
|
27641
27777
|
resetTrajectAvailability(state) {
|
|
27642
27778
|
state.trajectNodeAvailability = {};
|
|
27643
27779
|
state.trajectFlightAvailability = {};
|
|
27780
|
+
state.trajectDaySuggestions = {};
|
|
27644
27781
|
state.trajectEditLineGuid = null;
|
|
27782
|
+
state.trajectActivitiesDayOrder = null;
|
|
27783
|
+
state.trajectResolving = false;
|
|
27784
|
+
state.trajectItineraryView = false;
|
|
27645
27785
|
}
|
|
27646
27786
|
}
|
|
27647
27787
|
});
|
|
27648
|
-
const { setResults, setFilteredResults, setSelectedSearchResult, setPackagingAccoResults, setFilteredPackagingAccoResults, setFilteredPackagingFlightResults, setPackagingAccoSearchDetails, setSelectedPackagingAccoResult, setPackagingFlightResults, setSelectedPackagingFlight, setSelectedFlight, setSelectedFlightDetails, setBookingPackageDetails, selectFlight, setIsLoading, setFlightsLoading, setInitialFilters, setFilters, resetFilters, setInitialFlightFilters, setFlightFilters, resetFlightFilters, setSortType, setFlightSortType, setActiveTab, setCurrentPage, resetSearchState, setFlyInIsOpen, setEditablePackagingEntry, setTransactionId, setFlyInType, setPriceDetails, setItinerary, setSelectedOutwardKey, setSelectedReturnKey, resetFlightSelection, setExcursionSearchParams, setSelectedExcursionSearchResult, confirmExcursionForDay, removeConfirmedExcursionForDay, clearConfirmedExcursionsForDay, setBookPackagingEntry, setCurrentStep, setBookingNumber, setTrajectNodeLoading, setTrajectNodeAvailability, setTrajectNodeSelection, setTrajectFlightLoading, setTrajectFlightAvailability, setTrajectFlightSelection, setTrajectEditLineGuid, updateTrajectNodeResult, resetTrajectAvailability, updateEditableEntryLine, removeEditableEntryLines } = searchResultsSlice.actions;
|
|
27788
|
+
const { setResults, setFilteredResults, setSelectedSearchResult, setPackagingAccoResults, setFilteredPackagingAccoResults, setFilteredPackagingFlightResults, setPackagingAccoSearchDetails, setSelectedPackagingAccoResult, setPackagingFlightResults, setSelectedPackagingFlight, setSelectedFlight, setSelectedFlightDetails, setBookingPackageDetails, selectFlight, setIsLoading, setFlightsLoading, setInitialFilters, setFilters, resetFilters, setInitialFlightFilters, setFlightFilters, resetFlightFilters, setSortType, setFlightSortType, setActiveTab, setCurrentPage, resetSearchState, setFlyInIsOpen, setEditablePackagingEntry, setTransactionId, setFlyInType, setPriceDetails, setItinerary, setSelectedOutwardKey, setSelectedReturnKey, resetFlightSelection, setExcursionSearchParams, setSelectedExcursionSearchResult, confirmExcursionForDay, removeConfirmedExcursionForDay, clearConfirmedExcursionsForDay, setBookPackagingEntry, setCurrentStep, setBookingNumber, setTrajectNodeLoading, setTrajectNodeAvailability, setTrajectNodeSelection, setTrajectResolving, setTrajectItineraryView, setTrajectFlightLoading, setTrajectFlightAvailability, setTrajectFlightSelection, setTrajectEditLineGuid, setTrajectActivitiesDayOrder, setTrajectDaySuggestionsLoading, setTrajectDaySuggestions, addEditableEntryLines, updateTrajectNodeResult, resetTrajectAvailability, updateEditableEntryLine, removeEditableEntryLines } = searchResultsSlice.actions;
|
|
27649
27789
|
var searchResultsReducer = searchResultsSlice.reducer;
|
|
27650
27790
|
|
|
27651
27791
|
const ItemPicker = ({ items, selection, selectedSortByType, label, placeholder, classModifier, onPick, valueFormatter }) => {
|
|
@@ -27830,6 +27970,653 @@ const getDuration = (flight) => {
|
|
|
27830
27970
|
const numberOfNights = (segment) => {
|
|
27831
27971
|
return dateFns.differenceInCalendarDays(new Date(segment.to), new Date(segment.from));
|
|
27832
27972
|
};
|
|
27973
|
+
const CHARTER_SERVICE_TYPE = 10;
|
|
27974
|
+
const CRUISE_SERVICE_TYPE = 11;
|
|
27975
|
+
const FERRY_SERVICE_TYPE = 12;
|
|
27976
|
+
const TRANSFER_SERVICE_TYPE = 13;
|
|
27977
|
+
const BUS_SERVICE_TYPE = 14;
|
|
27978
|
+
const CAR_SERVICE_TYPE = 17;
|
|
27979
|
+
const CAMPER_SERVICE_TYPE = 18;
|
|
27980
|
+
const TRAIN_SERVICE_TYPE = 20;
|
|
27981
|
+
const RENTAL_SERVICE_TYPE = 22;
|
|
27982
|
+
const RESTAURANT_SERVICE_TYPE = 23;
|
|
27983
|
+
const EVENT_SERVICE_TYPE = 24;
|
|
27984
|
+
const TRAINING_SERVICE_TYPE = 25;
|
|
27985
|
+
/** Icon per service type, so every node on the itinerary reads at a glance. */
|
|
27986
|
+
const getServiceTypeIcon = (serviceType) => {
|
|
27987
|
+
switch (serviceType) {
|
|
27988
|
+
case ACCOMMODATION_SERVICE_TYPE:
|
|
27989
|
+
return 'ui-hotel';
|
|
27990
|
+
case EXCURSION_SERVICE_TYPE:
|
|
27991
|
+
return 'ui-excursion';
|
|
27992
|
+
case FLIGHT_SERVICE_TYPE:
|
|
27993
|
+
case CHARTER_SERVICE_TYPE:
|
|
27994
|
+
return 'ui-plane';
|
|
27995
|
+
case RESTAURANT_SERVICE_TYPE:
|
|
27996
|
+
return 'ui-utensils';
|
|
27997
|
+
case CRUISE_SERVICE_TYPE:
|
|
27998
|
+
case FERRY_SERVICE_TYPE:
|
|
27999
|
+
return 'ui-ship';
|
|
28000
|
+
case CAR_SERVICE_TYPE:
|
|
28001
|
+
case CAMPER_SERVICE_TYPE:
|
|
28002
|
+
case RENTAL_SERVICE_TYPE:
|
|
28003
|
+
return 'ui-car';
|
|
28004
|
+
case BUS_SERVICE_TYPE:
|
|
28005
|
+
return 'ui-bus';
|
|
28006
|
+
case TRAIN_SERVICE_TYPE:
|
|
28007
|
+
return 'ui-train';
|
|
28008
|
+
case TRANSFER_SERVICE_TYPE:
|
|
28009
|
+
return 'ui-transfer';
|
|
28010
|
+
case EVENT_SERVICE_TYPE:
|
|
28011
|
+
return 'ui-ticket';
|
|
28012
|
+
case TRAINING_SERVICE_TYPE:
|
|
28013
|
+
return 'ui-group';
|
|
28014
|
+
default:
|
|
28015
|
+
return 'ui-location';
|
|
28016
|
+
}
|
|
28017
|
+
};
|
|
28018
|
+
const getServiceTypeBadgeModifier = (serviceType) => {
|
|
28019
|
+
switch (serviceType) {
|
|
28020
|
+
case FLIGHT_SERVICE_TYPE:
|
|
28021
|
+
case CHARTER_SERVICE_TYPE:
|
|
28022
|
+
case CRUISE_SERVICE_TYPE:
|
|
28023
|
+
case FERRY_SERVICE_TYPE:
|
|
28024
|
+
return 'flight';
|
|
28025
|
+
case ACCOMMODATION_SERVICE_TYPE:
|
|
28026
|
+
return 'hotel';
|
|
28027
|
+
case EXCURSION_SERVICE_TYPE:
|
|
28028
|
+
case EVENT_SERVICE_TYPE:
|
|
28029
|
+
return 'excursion';
|
|
28030
|
+
case RESTAURANT_SERVICE_TYPE:
|
|
28031
|
+
return 'restaurant';
|
|
28032
|
+
default:
|
|
28033
|
+
return '';
|
|
28034
|
+
}
|
|
28035
|
+
};
|
|
28036
|
+
const POI_TRAJECT_NODE_TYPE = 2;
|
|
28037
|
+
const getTrajectNodeIcon = (node) => node.trajectNodeType === POI_TRAJECT_NODE_TYPE ? 'ui-location' : getServiceTypeIcon(node.serviceType);
|
|
28038
|
+
const getTrajectNodeBadgeModifier = (node) => node.trajectNodeType === POI_TRAJECT_NODE_TYPE ? 'poi' : getServiceTypeBadgeModifier(node.serviceType);
|
|
28039
|
+
|
|
28040
|
+
const REGULAR_TRAJECT_NODE_TYPE = 0;
|
|
28041
|
+
const CONNECTING_TRAJECT_NODE_TYPE = 1;
|
|
28042
|
+
const isSearchableTrajectNode = (node) => node.serviceType === ACCOMMODATION_SERVICE_TYPE || node.serviceType === EXCURSION_SERVICE_TYPE;
|
|
28043
|
+
const isTrajectFlightNode = (node) => node.serviceType === FLIGHT_SERVICE_TYPE;
|
|
28044
|
+
const getTrajectNodesWithLines = (trajectEntry) => {
|
|
28045
|
+
const linesByGuid = new Map((trajectEntry.entry.lines ?? []).map((line) => [line.guid, line]));
|
|
28046
|
+
return [...(trajectEntry.nodes ?? [])]
|
|
28047
|
+
.sort((a, b) => a.dayOrder - b.dayOrder || a.order - b.order)
|
|
28048
|
+
.map((node) => {
|
|
28049
|
+
const guids = node.lineGuids?.length ? node.lineGuids : [node.lineGuid];
|
|
28050
|
+
const lines = guids.map((guid) => linesByGuid.get(guid)).filter((x) => !!x);
|
|
28051
|
+
return { node, line: lines[0], lines };
|
|
28052
|
+
})
|
|
28053
|
+
.filter((x) => !!x.line);
|
|
28054
|
+
};
|
|
28055
|
+
const getCoordinates = (line, node, result) => {
|
|
28056
|
+
const sources = [
|
|
28057
|
+
...(result ? [result] : []),
|
|
28058
|
+
...(line ? [line] : []),
|
|
28059
|
+
...(node?.alternatives ?? []).filter((x) => x.isPreferred),
|
|
28060
|
+
...(node?.alternatives ?? [])
|
|
28061
|
+
];
|
|
28062
|
+
const found = sources.find((x) => !!x.latitude && !!x.longitude);
|
|
28063
|
+
return found ? { latitude: found.latitude, longitude: found.longitude } : null;
|
|
28064
|
+
};
|
|
28065
|
+
const buildDestination = (line, node, result) => {
|
|
28066
|
+
const coordinates = getCoordinates(line, node, result);
|
|
28067
|
+
const place = (result?.locationId && { id: result.locationId, isLocation: true }) ||
|
|
28068
|
+
(result?.oordId && { id: result.oordId, isOord: true }) ||
|
|
28069
|
+
(result?.regionId && { id: result.regionId, isRegion: true }) ||
|
|
28070
|
+
(result?.countryId && { id: result.countryId, isCountry: true }) ||
|
|
28071
|
+
(line.location?.id && { id: line.location.id, isLocation: true }) ||
|
|
28072
|
+
(line.oord?.id && { id: line.oord.id, isOord: true }) ||
|
|
28073
|
+
(line.region?.id && { id: line.region.id, isRegion: true }) ||
|
|
28074
|
+
(line.country?.id && { id: line.country.id, isCountry: true }) ||
|
|
28075
|
+
null;
|
|
28076
|
+
return { id: 0, ...place, ...coordinates };
|
|
28077
|
+
};
|
|
28078
|
+
const hasDestination = (destination) => !!destination && (!!destination.id || (!!destination.latitude && !!destination.longitude));
|
|
28079
|
+
const buildTrajectClientConfig = (context) => ({
|
|
28080
|
+
host: context.tideConnection.host,
|
|
28081
|
+
apiKey: context.tideConnection.apiKey
|
|
28082
|
+
});
|
|
28083
|
+
const buildTrajectSearchContext = (context, trajectEntry) => ({
|
|
28084
|
+
transactionId: trajectEntry.entry.transactionId,
|
|
28085
|
+
officeId: context.tideConnection.officeId ?? 1,
|
|
28086
|
+
catalogueId: context.searchConfiguration.defaultCatalogueId ?? context.tideConnection.catalogueIds?.[0] ?? 1,
|
|
28087
|
+
searchConfigurationId: context.searchConfiguration.id,
|
|
28088
|
+
portalId: context.portalId ?? null,
|
|
28089
|
+
agentId: context.agentId ?? null,
|
|
28090
|
+
language: context.languageCode ?? 'en-GB',
|
|
28091
|
+
rooms: getPackagingRoomsFromEntry(trajectEntry.entry)
|
|
28092
|
+
});
|
|
28093
|
+
const buildTrajectNodeRequest = ({ node, line }, context, productCode, destination) => ({
|
|
28094
|
+
transactionId: context.transactionId,
|
|
28095
|
+
officeId: context.officeId,
|
|
28096
|
+
agentId: context.agentId ?? null,
|
|
28097
|
+
catalogueId: context.catalogueId,
|
|
28098
|
+
searchConfigurationId: context.searchConfigurationId,
|
|
28099
|
+
portalId: context.portalId ?? null,
|
|
28100
|
+
vendorConfigurationId: node.externalVendorId ?? null,
|
|
28101
|
+
language: context.language,
|
|
28102
|
+
serviceType: node.serviceType,
|
|
28103
|
+
fromDate: toDateOnlyString(line.from),
|
|
28104
|
+
// An excursion happens on a day rather than over a span. The traject gives its line an end a day
|
|
28105
|
+
// out, the way a night is counted, which asks the search for a window it does not have.
|
|
28106
|
+
toDate: toDateOnlyString(node.serviceType === EXCURSION_SERVICE_TYPE ? line.from : line.to),
|
|
28107
|
+
destination: destination ?? buildDestination(line, node),
|
|
28108
|
+
productCode,
|
|
28109
|
+
rooms: context.rooms,
|
|
28110
|
+
tagIds: []
|
|
28111
|
+
});
|
|
28112
|
+
const coversDay = ({ node, line }, dayOrder) => {
|
|
28113
|
+
const nights = Math.max(dateFns.differenceInCalendarDays(new Date(line.to), new Date(line.from)), 1);
|
|
28114
|
+
return dayOrder >= node.dayOrder && dayOrder < node.dayOrder + nights;
|
|
28115
|
+
};
|
|
28116
|
+
const getDayDestinationItem = (items, dayOrder) => {
|
|
28117
|
+
const knowsWhereItIs = (x) => hasDestination(buildDestination(x.line, x.node));
|
|
28118
|
+
const stay = items.find((x) => x.node.serviceType === ACCOMMODATION_SERVICE_TYPE && coversDay(x, dayOrder) && knowsWhereItIs(x));
|
|
28119
|
+
if (stay)
|
|
28120
|
+
return stay;
|
|
28121
|
+
return ([...items]
|
|
28122
|
+
.filter((x) => x.node.dayOrder <= dayOrder && knowsWhereItIs(x))
|
|
28123
|
+
.sort((a, b) => a.node.dayOrder - b.node.dayOrder || a.node.order - b.node.order)
|
|
28124
|
+
.pop() ?? null);
|
|
28125
|
+
};
|
|
28126
|
+
const getDayDestination = (items, availability, dayOrder) => {
|
|
28127
|
+
const item = getDayDestinationItem(items, dayOrder);
|
|
28128
|
+
if (!item)
|
|
28129
|
+
return null;
|
|
28130
|
+
const resolved = availability[item.line.guid];
|
|
28131
|
+
const result = resolved?.results.find((x) => x.code === resolved.selectedCode) ?? null;
|
|
28132
|
+
const destination = buildDestination(item.line, item.node, result);
|
|
28133
|
+
return hasDestination(destination) ? destination : null;
|
|
28134
|
+
};
|
|
28135
|
+
const buildTrajectDayExcursionRequest = (date, destination, context) => ({
|
|
28136
|
+
transactionId: context.transactionId,
|
|
28137
|
+
officeId: context.officeId,
|
|
28138
|
+
agentId: context.agentId ?? null,
|
|
28139
|
+
catalogueId: context.catalogueId,
|
|
28140
|
+
searchConfigurationId: context.searchConfigurationId,
|
|
28141
|
+
portalId: context.portalId ?? null,
|
|
28142
|
+
vendorConfigurationId: null,
|
|
28143
|
+
language: context.language,
|
|
28144
|
+
serviceType: EXCURSION_SERVICE_TYPE,
|
|
28145
|
+
fromDate: toDateOnlyString(date),
|
|
28146
|
+
toDate: toDateOnlyString(date),
|
|
28147
|
+
destination,
|
|
28148
|
+
productCode: '',
|
|
28149
|
+
rooms: context.rooms,
|
|
28150
|
+
tagIds: []
|
|
28151
|
+
});
|
|
28152
|
+
const getExcursionOptionGroups = (result) => {
|
|
28153
|
+
const groups = new Map();
|
|
28154
|
+
(result.rooms ?? [])
|
|
28155
|
+
.flatMap((room) => room.options ?? [])
|
|
28156
|
+
.forEach((option) => {
|
|
28157
|
+
const key = [...(option.paxIds ?? [])].sort((a, b) => a - b).join(',');
|
|
28158
|
+
groups.set(key, [...(groups.get(key) ?? []), option]);
|
|
28159
|
+
});
|
|
28160
|
+
return [...groups.values()];
|
|
28161
|
+
};
|
|
28162
|
+
const excursionNeedsChoice = (result) => getExcursionOptionGroups(result).some((group) => group.length > 1 && !group.some((option) => option.isSelected));
|
|
28163
|
+
const buildTrajectExcursionLines = (result) => {
|
|
28164
|
+
const options = getExcursionOptionGroups(result)
|
|
28165
|
+
.map((group) => group.find((x) => x.isSelected) ?? (group.length === 1 ? group[0] : null))
|
|
28166
|
+
.filter((x) => !!x);
|
|
28167
|
+
if (!options.length)
|
|
28168
|
+
return [];
|
|
28169
|
+
const guids = options.map((option) => option.guid || crypto.randomUUID());
|
|
28170
|
+
return options.map((option, index) => ({
|
|
28171
|
+
guid: guids[index],
|
|
28172
|
+
moment: '',
|
|
28173
|
+
parentGuid: index === 0 ? null : guids[0],
|
|
28174
|
+
order: index,
|
|
28175
|
+
isChanged: true,
|
|
28176
|
+
from: result.fromDate,
|
|
28177
|
+
to: result.toDate,
|
|
28178
|
+
serviceType: EXCURSION_SERVICE_TYPE,
|
|
28179
|
+
productName: result.name,
|
|
28180
|
+
productCode: result.code,
|
|
28181
|
+
accommodationName: option.accommodationName,
|
|
28182
|
+
accommodationCode: option.accommodationCode,
|
|
28183
|
+
regimeName: option.regimeName,
|
|
28184
|
+
regimeCode: option.regimeCode,
|
|
28185
|
+
country: result.countryId ? { id: result.countryId, name: result.countryName, localizations: [] } : null,
|
|
28186
|
+
region: result.regionId ? { id: result.regionId, name: result.regionName, localizations: [] } : null,
|
|
28187
|
+
oord: result.oordId ? { id: result.oordId, name: result.oordName, localizations: [] } : null,
|
|
28188
|
+
location: result.locationId ? { id: result.locationId, name: result.locationName, localizations: [] } : null,
|
|
28189
|
+
longitude: result.longitude ?? null,
|
|
28190
|
+
latitude: result.latitude ?? null,
|
|
28191
|
+
pax: (option.paxIds ?? []).map((paxId, paxIndex) => ({ paxId, room: 0, order: paxIndex })),
|
|
28192
|
+
flightInformation: null
|
|
28193
|
+
}));
|
|
28194
|
+
};
|
|
28195
|
+
const buildExcursionSearchParams = (result) => ({
|
|
28196
|
+
date: result.fromDate,
|
|
28197
|
+
fromDate: result.fromDate,
|
|
28198
|
+
toDate: result.toDate,
|
|
28199
|
+
countryId: result.countryId,
|
|
28200
|
+
regionId: result.regionId,
|
|
28201
|
+
oordId: result.oordId,
|
|
28202
|
+
locationId: result.locationId,
|
|
28203
|
+
locationName: result.locationName,
|
|
28204
|
+
accommodationCode: null,
|
|
28205
|
+
accommodationName: null,
|
|
28206
|
+
hotelCode: result.code,
|
|
28207
|
+
hotelName: result.name,
|
|
28208
|
+
rooms: result.rooms ?? []
|
|
28209
|
+
});
|
|
28210
|
+
const buildDayActivityCards = (items, availability, suggestions) => {
|
|
28211
|
+
const cards = [];
|
|
28212
|
+
const seen = new Set();
|
|
28213
|
+
const push = (result, item) => {
|
|
28214
|
+
if (!result || seen.has(result.code))
|
|
28215
|
+
return;
|
|
28216
|
+
seen.add(result.code);
|
|
28217
|
+
cards.push({ result, item });
|
|
28218
|
+
};
|
|
28219
|
+
items.forEach((item) => {
|
|
28220
|
+
const results = availability[item.line.guid]?.results ?? [];
|
|
28221
|
+
const selected = results.find((x) => x.code === availability[item.line.guid]?.selectedCode);
|
|
28222
|
+
push(selected, item);
|
|
28223
|
+
results.filter((x) => !!findCandidateForResult(item.node, x)).forEach((result) => push(result, item));
|
|
28224
|
+
});
|
|
28225
|
+
suggestions.forEach((result) => push(result));
|
|
28226
|
+
items.forEach((item) => (availability[item.line.guid]?.results ?? []).forEach((result) => push(result)));
|
|
28227
|
+
return cards;
|
|
28228
|
+
};
|
|
28229
|
+
/**
|
|
28230
|
+
* Where to search for a node.
|
|
28231
|
+
*
|
|
28232
|
+
* Normally the node says so itself, through the product it was configured with. An excursion slot
|
|
28233
|
+
* left empty on the traject says nothing at all — no code, no alternatives, and a line with no
|
|
28234
|
+
* place on it — so it falls back to wherever the party is staying that day, the same anchor the
|
|
28235
|
+
* day's own activity search uses.
|
|
28236
|
+
*/
|
|
28237
|
+
const getNodeSearchDestination = (items, item) => {
|
|
28238
|
+
const own = buildDestination(item.line, item.node);
|
|
28239
|
+
if (hasDestination(own))
|
|
28240
|
+
return own;
|
|
28241
|
+
return getDayDestination(items, {}, item.node.dayOrder) ?? own;
|
|
28242
|
+
};
|
|
28243
|
+
const getConfiguredExcursionCodes = (items, dayOrder) => new Set(items
|
|
28244
|
+
.filter((x) => x.node.dayOrder === dayOrder && x.node.serviceType === EXCURSION_SERVICE_TYPE)
|
|
28245
|
+
.flatMap((x) => [x.node.code, ...getNodeCandidates(x.node).map((candidate) => candidate.code)])
|
|
28246
|
+
.filter((code) => !!code));
|
|
28247
|
+
const getNodeCandidates = (node) => {
|
|
28248
|
+
const configured = (node.alternatives ?? []).filter((x) => !!x.code && x.code.trim().length > 0);
|
|
28249
|
+
if (configured.length)
|
|
28250
|
+
return configured;
|
|
28251
|
+
const fallbackCode = node.code ?? node.preferredAccommodationCode;
|
|
28252
|
+
if (!fallbackCode || !fallbackCode.trim().length)
|
|
28253
|
+
return [];
|
|
28254
|
+
return [
|
|
28255
|
+
{
|
|
28256
|
+
code: fallbackCode,
|
|
28257
|
+
name: node.name,
|
|
28258
|
+
description: node.description,
|
|
28259
|
+
imageUrl: node.imageUrl,
|
|
28260
|
+
contentSource: node.contentSource,
|
|
28261
|
+
vendor: node.vendor,
|
|
28262
|
+
externalVendorId: node.externalVendorId,
|
|
28263
|
+
isPreferred: true
|
|
28264
|
+
}
|
|
28265
|
+
];
|
|
28266
|
+
};
|
|
28267
|
+
const findCandidateForResult = (node, result) => getNodeCandidates(node).find((candidate) => matchesCode(result, candidate.code)) ?? null;
|
|
28268
|
+
const matchesCode = (result, code) => {
|
|
28269
|
+
if (result.code === code)
|
|
28270
|
+
return true;
|
|
28271
|
+
return (result.rooms ?? []).some((room) => (room.options ?? []).some((option) => option.accommodationCode === code));
|
|
28272
|
+
};
|
|
28273
|
+
const getPackagingRoomsFromEntry = (entry) => {
|
|
28274
|
+
const paxById = new Map((entry.pax ?? []).map((p) => [p.id, p]));
|
|
28275
|
+
const rooms = (entry.rooms ?? []).map((room) => ({
|
|
28276
|
+
travellers: (room.paxIds ?? []).map((paxId) => {
|
|
28277
|
+
const pax = paxById.get(paxId);
|
|
28278
|
+
return {
|
|
28279
|
+
id: paxId,
|
|
28280
|
+
age: pax?.age ?? null,
|
|
28281
|
+
dateOfBirth: pax?.dateOfBirth ?? null
|
|
28282
|
+
};
|
|
28283
|
+
})
|
|
28284
|
+
}));
|
|
28285
|
+
return rooms.filter((room) => room.travellers.length > 0);
|
|
28286
|
+
};
|
|
28287
|
+
const pickCheapest = (results) => [...results].sort((a, b) => a.price - b.price)[0] ?? null;
|
|
28288
|
+
const applyResultToLines = (lines, result) => lines.map((line, index) => applyResultToLine(line, result, index));
|
|
28289
|
+
const getOptionForRoom = (result, roomIndex) => {
|
|
28290
|
+
const room = (result.rooms ?? [])[roomIndex];
|
|
28291
|
+
if (room)
|
|
28292
|
+
return (room.options ?? []).find((x) => x.isSelected) ?? (room.options ?? [])[0];
|
|
28293
|
+
return (result.rooms ?? []).flatMap((x) => x.options ?? []).find((x) => x.isSelected) ?? (result.rooms ?? [])[0]?.options?.[0];
|
|
28294
|
+
};
|
|
28295
|
+
const applyResultToLine = (line, result, roomIndex = 0) => {
|
|
28296
|
+
const option = getOptionForRoom(result, roomIndex);
|
|
28297
|
+
return {
|
|
28298
|
+
...line,
|
|
28299
|
+
productName: result.name ?? line.productName,
|
|
28300
|
+
productCode: result.code ?? line.productCode,
|
|
28301
|
+
accommodationCode: option?.accommodationCode ?? line.accommodationCode,
|
|
28302
|
+
accommodationName: option?.accommodationName ?? line.accommodationName,
|
|
28303
|
+
regimeCode: option?.regimeCode ?? line.regimeCode,
|
|
28304
|
+
regimeName: option?.regimeName ?? line.regimeName,
|
|
28305
|
+
latitude: result.latitude ?? line.latitude,
|
|
28306
|
+
longitude: result.longitude ?? line.longitude,
|
|
28307
|
+
isChanged: true
|
|
28308
|
+
};
|
|
28309
|
+
};
|
|
28310
|
+
const toDateOnlyUtcString = (value) => {
|
|
28311
|
+
const date = new Date(value);
|
|
28312
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
|
|
28313
|
+
};
|
|
28314
|
+
const toTimeOnlyString = (value) => {
|
|
28315
|
+
const date = new Date(value);
|
|
28316
|
+
const hh = String(date.getUTCHours()).padStart(2, '0');
|
|
28317
|
+
const mm = String(date.getUTCMinutes()).padStart(2, '0');
|
|
28318
|
+
const ss = String(date.getUTCSeconds()).padStart(2, '0');
|
|
28319
|
+
return `${hh}:${mm}:${ss}`;
|
|
28320
|
+
};
|
|
28321
|
+
const mapFlightSegmentsToFlightLines = (segments) => segments.map((segment) => ({
|
|
28322
|
+
airlineCode: segment.marketingAirlineCode,
|
|
28323
|
+
airlineDescription: segment.marketingAirlineName,
|
|
28324
|
+
operatingAirlineCode: segment.operatingAirlineCode,
|
|
28325
|
+
operatingAirlineDescription: segment.operatingAirlineName,
|
|
28326
|
+
flightNumber: segment.flightNumber,
|
|
28327
|
+
operatingFlightNumber: segment.operatingFlightNumber ?? null,
|
|
28328
|
+
departureDate: toDateOnlyUtcString(segment.departureDateTime),
|
|
28329
|
+
departureTime: toTimeOnlyString(segment.departureDateTime),
|
|
28330
|
+
departureAirportCode: segment.departureAirportCode,
|
|
28331
|
+
departureAirportDescription: segment.departureAirportName,
|
|
28332
|
+
arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
|
|
28333
|
+
arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
|
|
28334
|
+
arrivalAirportCode: segment.arrivalAirportCode,
|
|
28335
|
+
arrivalAirportDescription: segment.arrivalAirportName,
|
|
28336
|
+
durationInTicks: segment.durationInTicks
|
|
28337
|
+
}));
|
|
28338
|
+
const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
|
|
28339
|
+
transactionId: context.transactionId,
|
|
28340
|
+
officeId: context.officeId,
|
|
28341
|
+
catalogueId: context.catalogueId,
|
|
28342
|
+
agentId: context.agentId ?? null,
|
|
28343
|
+
language: context.language,
|
|
28344
|
+
departureAirportCode: node.departureAirportCode ?? '',
|
|
28345
|
+
arrivalAirportCode: node.arrivalAirportCode ?? '',
|
|
28346
|
+
returnAirportCode: null,
|
|
28347
|
+
luggageIncluded: null,
|
|
28348
|
+
maxStops: null,
|
|
28349
|
+
travelClass: null,
|
|
28350
|
+
vendorConfigurationId: node.externalVendorId ?? null,
|
|
28351
|
+
pax,
|
|
28352
|
+
outward: { date: dateToDateStruct(new Date(line.from)) },
|
|
28353
|
+
return: null
|
|
28354
|
+
});
|
|
28355
|
+
/**
|
|
28356
|
+
* The entry as the backend can price it.
|
|
28357
|
+
*
|
|
28358
|
+
* A search caches an EntryLine per option and hands back that line's guid on the option. The
|
|
28359
|
+
* backend then rebuilds the entry by looking each line up in that cache on its guid, so a line
|
|
28360
|
+
* it cannot find is dropped — PackagingEntryBuilder.SyncEntryLines skips it, and an entry whose
|
|
28361
|
+
* lines all came off a traject prices as nothing at all.
|
|
28362
|
+
*
|
|
28363
|
+
* A traject entry is rendered before any search runs, so its lines carry the guids the traject
|
|
28364
|
+
* mapper gave them. This swaps in the guid of whatever each line resolved onto, leaving the
|
|
28365
|
+
* lines the traveller added themselves alone — those were built from a search result and already
|
|
28366
|
+
* carry the right one.
|
|
28367
|
+
*/
|
|
28368
|
+
const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightAvailability) => {
|
|
28369
|
+
const cachedGuids = new Map();
|
|
28370
|
+
getTrajectNodesWithLines(trajectEntry).forEach((item) => {
|
|
28371
|
+
if (isTrajectFlightNode(item.node)) {
|
|
28372
|
+
// A flight search caches the outward line under the guid it reports as outwardGuid.
|
|
28373
|
+
const guid = flightAvailability[item.line.guid]?.selectedGuid;
|
|
28374
|
+
if (guid)
|
|
28375
|
+
cachedGuids.set(item.line.guid, guid);
|
|
28376
|
+
return;
|
|
28377
|
+
}
|
|
28378
|
+
const availability = nodeAvailability[item.line.guid];
|
|
28379
|
+
const result = availability?.results.find((x) => x.code === availability.selectedCode);
|
|
28380
|
+
if (!result)
|
|
28381
|
+
return;
|
|
28382
|
+
// Room i took option i when the result was stamped on, so it takes option i here too.
|
|
28383
|
+
item.lines.forEach((line, index) => {
|
|
28384
|
+
const guid = getOptionForRoom(result, index)?.guid;
|
|
28385
|
+
if (guid)
|
|
28386
|
+
cachedGuids.set(line.guid, guid);
|
|
28387
|
+
});
|
|
28388
|
+
});
|
|
28389
|
+
if (!cachedGuids.size)
|
|
28390
|
+
return entry;
|
|
28391
|
+
return {
|
|
28392
|
+
...entry,
|
|
28393
|
+
lines: (entry.lines ?? []).map((line) => ({
|
|
28394
|
+
...line,
|
|
28395
|
+
guid: cachedGuids.get(line.guid) ?? line.guid,
|
|
28396
|
+
parentGuid: (line.parentGuid && cachedGuids.get(line.parentGuid)) ?? line.parentGuid
|
|
28397
|
+
}))
|
|
28398
|
+
};
|
|
28399
|
+
};
|
|
28400
|
+
const pickCheapestFlight = (flights) => [...flights].sort((a, b) => a.price - b.price)[0] ?? null;
|
|
28401
|
+
const applyFlightToLine = (line, flight) => {
|
|
28402
|
+
const segments = flight.outward?.segments ?? [];
|
|
28403
|
+
if (!segments.length)
|
|
28404
|
+
return line;
|
|
28405
|
+
const firstSegment = segments[0];
|
|
28406
|
+
const lastSegment = segments[segments.length - 1];
|
|
28407
|
+
return {
|
|
28408
|
+
...line,
|
|
28409
|
+
from: new Date(firstSegment.departureDateTime).toISOString(),
|
|
28410
|
+
to: new Date(lastSegment.arrivalDateTime).toISOString(),
|
|
28411
|
+
productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
|
|
28412
|
+
productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`,
|
|
28413
|
+
accommodationName: firstSegment.metaData?.farePriceClassName ?? line.accommodationName,
|
|
28414
|
+
accommodationCode: firstSegment.metaData?.fareCode ?? line.accommodationCode,
|
|
28415
|
+
flightInformation: {
|
|
28416
|
+
pnr: '',
|
|
28417
|
+
flightLines: mapFlightSegmentsToFlightLines(segments)
|
|
28418
|
+
},
|
|
28419
|
+
isChanged: true
|
|
28420
|
+
};
|
|
28421
|
+
};
|
|
28422
|
+
/**
|
|
28423
|
+
* Externally sourced products carry a vendor configuration; products held internally in Tide have
|
|
28424
|
+
* none. `vendorType` is not a reliable signal — the packaging service nulls it out when the search
|
|
28425
|
+
* configuration has ExposeProvider off — so the vendor ids are what we go on.
|
|
28426
|
+
*/
|
|
28427
|
+
const isExternalResult = (result) => result.vendorId != null || result.vendorConfigurationId != null;
|
|
28428
|
+
const isExternalCandidate = (candidate) => candidate.externalVendorId != null || candidate.vendor != null;
|
|
28429
|
+
|
|
28430
|
+
const badgeClass = (kind, modifier) => {
|
|
28431
|
+
const base = `search__filter__itinerary__${kind}-badge`;
|
|
28432
|
+
const fallback = kind === 'segment' ? `${base}--secondary` : `${base}--default`;
|
|
28433
|
+
return `${base} ${modifier ? `${base}--${modifier}` : fallback}`;
|
|
28434
|
+
};
|
|
28435
|
+
const nightsOf = (node, line) => dateFns.differenceInCalendarDays(new Date(line?.to ?? node.to), new Date(line?.from ?? node.from));
|
|
28436
|
+
const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
|
|
28437
|
+
const timeline = React.useMemo(() => {
|
|
28438
|
+
const linesByGuid = new Map((packagingEntry.lines ?? []).map((line) => [line.guid, line]));
|
|
28439
|
+
const nodes = [...(trajectEntry.nodes ?? [])]
|
|
28440
|
+
.sort((a, b) => a.dayOrder - b.dayOrder || a.order - b.order)
|
|
28441
|
+
.map((node) => {
|
|
28442
|
+
const guids = node.lineGuids?.length ? node.lineGuids : [node.lineGuid];
|
|
28443
|
+
const lines = guids.map((guid) => linesByGuid.get(guid)).filter((x) => !!x);
|
|
28444
|
+
return { node, lines };
|
|
28445
|
+
});
|
|
28446
|
+
const claimed = new Set(nodes.flatMap((item) => item.lines.map((line) => line.guid)));
|
|
28447
|
+
const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid));
|
|
28448
|
+
if (!extras.length)
|
|
28449
|
+
return nodes;
|
|
28450
|
+
const dayOrderOf = (date) => trajectEntry.traject.days.find((day) => dateFns.isSameDay(new Date(day.date), new Date(date)))?.order ??
|
|
28451
|
+
Math.max(dateFns.differenceInCalendarDays(new Date(date), new Date(trajectEntry.traject.startDate)), 0);
|
|
28452
|
+
// One product bought once, however many option lines it took (an adult and a child ticket).
|
|
28453
|
+
const grouped = new Map();
|
|
28454
|
+
extras.forEach((line) => {
|
|
28455
|
+
const key = `${line.productCode}|${line.from}`;
|
|
28456
|
+
grouped.set(key, [...(grouped.get(key) ?? []), line]);
|
|
28457
|
+
});
|
|
28458
|
+
const byDay = new Map();
|
|
28459
|
+
nodes.forEach((item) => byDay.set(item.node.dayOrder, [...(byDay.get(item.node.dayOrder) ?? []), item]));
|
|
28460
|
+
[...grouped.values()].forEach((lines, index) => {
|
|
28461
|
+
const line = lines[0];
|
|
28462
|
+
const dayOrder = dayOrderOf(line.from);
|
|
28463
|
+
const item = {
|
|
28464
|
+
lines,
|
|
28465
|
+
node: {
|
|
28466
|
+
nodeId: -(index + 1),
|
|
28467
|
+
lineGuid: line.guid,
|
|
28468
|
+
lineGuids: lines.map((x) => x.guid),
|
|
28469
|
+
dayOrder,
|
|
28470
|
+
order: Number.MAX_SAFE_INTEGER,
|
|
28471
|
+
name: line.productName,
|
|
28472
|
+
serviceType: line.serviceType,
|
|
28473
|
+
trajectNodeType: REGULAR_TRAJECT_NODE_TYPE,
|
|
28474
|
+
requirementType: 0,
|
|
28475
|
+
contentSource: 0,
|
|
28476
|
+
alternatives: [],
|
|
28477
|
+
from: line.from,
|
|
28478
|
+
to: line.to
|
|
28479
|
+
}
|
|
28480
|
+
};
|
|
28481
|
+
const dayItems = [...(byDay.get(dayOrder) ?? [])];
|
|
28482
|
+
let at = dayItems.length;
|
|
28483
|
+
while (at > 0 && dayItems[at - 1].node.trajectNodeType === CONNECTING_TRAJECT_NODE_TYPE)
|
|
28484
|
+
at--;
|
|
28485
|
+
dayItems.splice(at, 0, item);
|
|
28486
|
+
byDay.set(dayOrder, dayItems);
|
|
28487
|
+
});
|
|
28488
|
+
return [...byDay.keys()].sort((a, b) => a - b).flatMap((dayOrder) => byDay.get(dayOrder));
|
|
28489
|
+
}, [trajectEntry, packagingEntry]);
|
|
28490
|
+
const stayByDay = React.useMemo(() => {
|
|
28491
|
+
const map = new Map();
|
|
28492
|
+
timeline
|
|
28493
|
+
.filter(({ node }) => node.trajectNodeType === REGULAR_TRAJECT_NODE_TYPE && node.serviceType === ACCOMMODATION_SERVICE_TYPE)
|
|
28494
|
+
.forEach(({ node, lines }) => {
|
|
28495
|
+
const line = lines[0];
|
|
28496
|
+
const nights = Math.max(nightsOf(node, line), 1);
|
|
28497
|
+
const name = line?.productName || node.preferredAccommodationName || node.name || '';
|
|
28498
|
+
for (let night = 0; night < nights; night++)
|
|
28499
|
+
map.set(node.dayOrder + night, { name, night, nights });
|
|
28500
|
+
});
|
|
28501
|
+
return map;
|
|
28502
|
+
}, [timeline]);
|
|
28503
|
+
const renderDate = (value) => (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" },
|
|
28504
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-date-date" },
|
|
28505
|
+
React__default["default"].createElement("strong", null, dateFns.format(new Date(value), 'd'))),
|
|
28506
|
+
React__default["default"].createElement("p", null, dateFns.format(new Date(value), 'MMM'))));
|
|
28507
|
+
const renderConnectingNode = ({ node }) => {
|
|
28508
|
+
const distance = node.distanceInKm ? `${Math.round(node.distanceInKm)} km` : '';
|
|
28509
|
+
return (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport", key: `connect-${node.nodeId}` },
|
|
28510
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline" },
|
|
28511
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
|
|
28512
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-item" },
|
|
28513
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" }),
|
|
28514
|
+
React__default["default"].createElement("div", { className: badgeClass('transport', getTrajectNodeBadgeModifier(node)) },
|
|
28515
|
+
React__default["default"].createElement(Icon, { name: getTrajectNodeIcon(node), height: 15 })),
|
|
28516
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-details" },
|
|
28517
|
+
React__default["default"].createElement("h6", null, node.name || distance),
|
|
28518
|
+
!!node.name && !!distance && React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-time" }, distance)))));
|
|
28519
|
+
};
|
|
28520
|
+
const renderOvernight = (item, index) => {
|
|
28521
|
+
const { dayOrder } = item.node;
|
|
28522
|
+
const closesTheDay = timeline[index + 1]?.node.dayOrder !== dayOrder;
|
|
28523
|
+
const stay = stayByDay.get(dayOrder);
|
|
28524
|
+
if (!closesTheDay || !stay)
|
|
28525
|
+
return null;
|
|
28526
|
+
return (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport", key: `overnight-${item.node.nodeId}` },
|
|
28527
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline" },
|
|
28528
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
|
|
28529
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-item" },
|
|
28530
|
+
renderDate(item.node.from),
|
|
28531
|
+
React__default["default"].createElement("div", { className: badgeClass('transport', getServiceTypeBadgeModifier(ACCOMMODATION_SERVICE_TYPE)) },
|
|
28532
|
+
React__default["default"].createElement(Icon, { name: "ui-bed", height: 15 })),
|
|
28533
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-details" },
|
|
28534
|
+
React__default["default"].createElement("h6", null, stay.name),
|
|
28535
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-time" },
|
|
28536
|
+
stay.night + 1,
|
|
28537
|
+
" ",
|
|
28538
|
+
translations.SRP.OF,
|
|
28539
|
+
" ",
|
|
28540
|
+
stay.nights,
|
|
28541
|
+
" ",
|
|
28542
|
+
translations.SRP.NIGHTS)))));
|
|
28543
|
+
};
|
|
28544
|
+
const renderFlightNode = ({ node, lines }) => {
|
|
28545
|
+
const line = lines[0];
|
|
28546
|
+
return (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport", key: `flight-${node.nodeId}` },
|
|
28547
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline" },
|
|
28548
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
|
|
28549
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-item" },
|
|
28550
|
+
renderDate(line?.from ?? node.from),
|
|
28551
|
+
React__default["default"].createElement("div", { className: badgeClass('transport', getServiceTypeBadgeModifier(FLIGHT_SERVICE_TYPE)) },
|
|
28552
|
+
React__default["default"].createElement(Icon, { name: "ui-plane", height: 15 })),
|
|
28553
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-details" },
|
|
28554
|
+
React__default["default"].createElement("h6", null, line?.productName || node.name),
|
|
28555
|
+
!!getDepartureTime(line) && (React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-plane" },
|
|
28556
|
+
React__default["default"].createElement("span", null,
|
|
28557
|
+
React__default["default"].createElement(Icon, { name: "ui-plane-depart", height: 14 }),
|
|
28558
|
+
" ",
|
|
28559
|
+
React__default["default"].createElement("strong", null, getDepartureTime(line))),
|
|
28560
|
+
' ',
|
|
28561
|
+
"-",
|
|
28562
|
+
' ',
|
|
28563
|
+
React__default["default"].createElement("span", null,
|
|
28564
|
+
React__default["default"].createElement(Icon, { name: "ui-plane-arrive", height: 14 }),
|
|
28565
|
+
" ",
|
|
28566
|
+
React__default["default"].createElement("strong", null, getArrivalTime(line))))),
|
|
28567
|
+
!!getDuration(line) && (React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-time" },
|
|
28568
|
+
React__default["default"].createElement("span", null,
|
|
28569
|
+
React__default["default"].createElement(Icon, { name: "ui-clock", height: 20 }),
|
|
28570
|
+
" ",
|
|
28571
|
+
getDuration(line))))))));
|
|
28572
|
+
};
|
|
28573
|
+
const renderServiceNode = ({ node, lines }) => {
|
|
28574
|
+
const line = lines[0];
|
|
28575
|
+
const isStay = node.serviceType === ACCOMMODATION_SERVICE_TYPE;
|
|
28576
|
+
const nights = isStay ? nightsOf(node, line) : 0;
|
|
28577
|
+
return (React__default["default"].createElement("div", { className: "search__filter__itinerary__segments", key: `node-${node.nodeId}` },
|
|
28578
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segments__wrapper" },
|
|
28579
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segments-timeline" },
|
|
28580
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segments-timeline-line" })),
|
|
28581
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment" },
|
|
28582
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-item" },
|
|
28583
|
+
React__default["default"].createElement("div", { className: `search__filter__itinerary__segment-date ${isStay && nights > 0 ? 'search__filter__itinerary__segment-nights' : ''}` }, isStay && nights > 0 ? (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
28584
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__segment-date-date" },
|
|
28585
|
+
React__default["default"].createElement("strong", null, nights)),
|
|
28586
|
+
React__default["default"].createElement(Icon, { name: "ui-moon", width: 16, height: 16 }))) : (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
28587
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__segment-date-date" },
|
|
28588
|
+
React__default["default"].createElement("strong", null, dateFns.format(new Date(line?.from ?? node.from), 'd'))),
|
|
28589
|
+
React__default["default"].createElement("p", null, dateFns.format(new Date(line?.from ?? node.from), 'MMM'))))),
|
|
28590
|
+
React__default["default"].createElement("div", { className: badgeClass('segment', getTrajectNodeBadgeModifier(node)) },
|
|
28591
|
+
React__default["default"].createElement(Icon, { name: getTrajectNodeIcon(node), width: 15, height: 15 })),
|
|
28592
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details" },
|
|
28593
|
+
React__default["default"].createElement("h6", null, line?.productName || node.preferredAccommodationName || node.name),
|
|
28594
|
+
isStay &&
|
|
28595
|
+
lines.map((roomLine, index) => (React__default["default"].createElement(React__default["default"].Fragment, { key: roomLine.guid },
|
|
28596
|
+
lines.length > 1 && (React__default["default"].createElement("strong", null,
|
|
28597
|
+
translations.SHARED.ROOM,
|
|
28598
|
+
"\u00A0",
|
|
28599
|
+
index + 1)),
|
|
28600
|
+
roomLine.accommodationName && (React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details__room" },
|
|
28601
|
+
React__default["default"].createElement(Icon, { name: "ui-bed", width: 12, height: 12 }),
|
|
28602
|
+
roomLine.accommodationName)),
|
|
28603
|
+
roomLine.regimeName && (React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details__room" },
|
|
28604
|
+
React__default["default"].createElement(Icon, { name: "ui-utensils", width: 12, height: 12 }),
|
|
28605
|
+
roomLine.regimeName))))),
|
|
28606
|
+
!isStay && React__default["default"].createElement("p", { className: "search__filter__itinerary__segment-details-text" }, dateFns.format(new Date(line?.from ?? node.from), 'EEE. d MMM yyyy'))))))));
|
|
28607
|
+
};
|
|
28608
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null, timeline.map((item, index) => {
|
|
28609
|
+
if (item.node.trajectNodeType === CONNECTING_TRAJECT_NODE_TYPE) {
|
|
28610
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, { key: `connect-${item.node.nodeId}` },
|
|
28611
|
+
renderConnectingNode(item),
|
|
28612
|
+
renderOvernight(item, index)));
|
|
28613
|
+
}
|
|
28614
|
+
if (item.node.trajectNodeType === REGULAR_TRAJECT_NODE_TYPE && item.node.serviceType === FLIGHT_SERVICE_TYPE)
|
|
28615
|
+
return renderFlightNode(item);
|
|
28616
|
+
return renderServiceNode(item);
|
|
28617
|
+
})));
|
|
28618
|
+
};
|
|
28619
|
+
|
|
27833
28620
|
const getSegmentIcon = (segment) => {
|
|
27834
28621
|
switch (segment.serviceType) {
|
|
27835
28622
|
case 3:
|
|
@@ -27953,116 +28740,119 @@ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation })
|
|
|
27953
28740
|
dateFns.format(new Date(firstEntryLine.from), 'EEE. d MMM yyyy'),
|
|
27954
28741
|
" - ",
|
|
27955
28742
|
React__default["default"].createElement("strong", null, translations.SRP.START))))),
|
|
27956
|
-
|
|
27957
|
-
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport
|
|
27958
|
-
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline
|
|
27959
|
-
|
|
27960
|
-
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-
|
|
27961
|
-
React__default["default"].createElement("
|
|
27962
|
-
React__default["default"].createElement("
|
|
27963
|
-
|
|
27964
|
-
|
|
27965
|
-
React__default["default"].createElement(
|
|
27966
|
-
|
|
27967
|
-
React__default["default"].createElement("
|
|
27968
|
-
|
|
27969
|
-
React__default["default"].createElement("
|
|
27970
|
-
React__default["default"].createElement(
|
|
27971
|
-
|
|
27972
|
-
|
|
27973
|
-
|
|
27974
|
-
|
|
27975
|
-
|
|
27976
|
-
|
|
27977
|
-
React__default["default"].createElement(
|
|
27978
|
-
|
|
27979
|
-
|
|
27980
|
-
|
|
27981
|
-
React__default["default"].createElement("
|
|
27982
|
-
React__default["default"].createElement(
|
|
27983
|
-
|
|
27984
|
-
|
|
27985
|
-
|
|
27986
|
-
|
|
27987
|
-
|
|
27988
|
-
|
|
27989
|
-
|
|
27990
|
-
|
|
27991
|
-
React__default["default"].createElement("div", { className: "
|
|
27992
|
-
React__default["default"].createElement("div", { className: "
|
|
27993
|
-
React__default["default"].createElement("div", { className: "search__filter__itinerary__segments-timeline
|
|
27994
|
-
|
|
27995
|
-
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment
|
|
27996
|
-
React__default["default"].createElement("div", { className: "
|
|
27997
|
-
React__default["default"].createElement("
|
|
27998
|
-
React__default["default"].createElement("
|
|
27999
|
-
|
|
28000
|
-
|
|
28001
|
-
React__default["default"].createElement(
|
|
28002
|
-
|
|
28003
|
-
React__default["default"].createElement("
|
|
28004
|
-
|
|
28005
|
-
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28010
|
-
|
|
28011
|
-
|
|
28012
|
-
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
|
|
28016
|
-
|
|
28017
|
-
|
|
28018
|
-
|
|
28019
|
-
|
|
28020
|
-
React__default["default"].createElement(
|
|
28021
|
-
|
|
28022
|
-
|
|
28023
|
-
|
|
28024
|
-
firstSegment
|
|
28025
|
-
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28032
|
-
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
|
|
28038
|
-
|
|
28039
|
-
|
|
28040
|
-
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport
|
|
28041
|
-
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-
|
|
28042
|
-
React__default["default"].createElement("
|
|
28043
|
-
|
|
28044
|
-
React__default["default"].createElement("
|
|
28045
|
-
|
|
28046
|
-
|
|
28047
|
-
|
|
28048
|
-
React__default["default"].createElement("
|
|
28049
|
-
|
|
28050
|
-
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
React__default["default"].createElement("
|
|
28054
|
-
|
|
28055
|
-
|
|
28056
|
-
|
|
28057
|
-
|
|
28058
|
-
|
|
28059
|
-
|
|
28060
|
-
React__default["default"].createElement("
|
|
28061
|
-
|
|
28062
|
-
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28743
|
+
context?.trajectEntry ? (React__default["default"].createElement(TrajectItinerary, { trajectEntry: context.trajectEntry, packagingEntry: packagingEntry, translations: translations })) : (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
28744
|
+
outboundFlight && (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport" },
|
|
28745
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline" },
|
|
28746
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
|
|
28747
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-item" },
|
|
28748
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" },
|
|
28749
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-date-date" },
|
|
28750
|
+
React__default["default"].createElement("strong", null, dateFns.format(new Date(outboundFlight.from), 'd'))),
|
|
28751
|
+
React__default["default"].createElement("p", null, dateFns.format(new Date(outboundFlight.from), 'MMM'))),
|
|
28752
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-badge" },
|
|
28753
|
+
React__default["default"].createElement(Icon, { name: "ui-plane", height: 15 })),
|
|
28754
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-details" },
|
|
28755
|
+
React__default["default"].createElement("h6", null, outboundFlight.productName),
|
|
28756
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-plane" },
|
|
28757
|
+
React__default["default"].createElement("span", null,
|
|
28758
|
+
React__default["default"].createElement(Icon, { name: "ui-plane-depart", height: 14 }),
|
|
28759
|
+
" ",
|
|
28760
|
+
React__default["default"].createElement("strong", null, getDepartureTime(outboundFlight))),
|
|
28761
|
+
' ',
|
|
28762
|
+
"-",
|
|
28763
|
+
' ',
|
|
28764
|
+
React__default["default"].createElement("span", null,
|
|
28765
|
+
React__default["default"].createElement(Icon, { name: "ui-plane-arrive", height: 14 }),
|
|
28766
|
+
" ",
|
|
28767
|
+
React__default["default"].createElement("strong", null, getArrivalTime(outboundFlight)))),
|
|
28768
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-time" },
|
|
28769
|
+
React__default["default"].createElement("span", null,
|
|
28770
|
+
React__default["default"].createElement(Icon, { name: "ui-clock", height: 20 }),
|
|
28771
|
+
" ",
|
|
28772
|
+
getDuration(outboundFlight))))))),
|
|
28773
|
+
!lodash.isEmpty(groupedOtherSegments) &&
|
|
28774
|
+
groupedOtherSegments.map((group) => {
|
|
28775
|
+
const firstSegment = lodash.first(group.segments);
|
|
28776
|
+
if (!firstSegment)
|
|
28777
|
+
return null;
|
|
28778
|
+
return (React__default["default"].createElement("div", { className: "search__filter__itinerary__segments" },
|
|
28779
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segments__wrapper", key: group.key },
|
|
28780
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segments-timeline" },
|
|
28781
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segments-timeline-line" })),
|
|
28782
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment" },
|
|
28783
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-item search__filter__itinerary__segment-item--start" },
|
|
28784
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" },
|
|
28785
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-date-date" },
|
|
28786
|
+
React__default["default"].createElement("strong", null, dateFns.format(new Date(firstSegment.from), 'd'))),
|
|
28787
|
+
React__default["default"].createElement("p", null, dateFns.format(new Date(firstSegment.from), 'MMM'))),
|
|
28788
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-badge" },
|
|
28789
|
+
React__default["default"].createElement(Icon, { name: "ui-location", width: 15, height: 15 })),
|
|
28790
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details" },
|
|
28791
|
+
React__default["default"].createElement("h6", null,
|
|
28792
|
+
firstSegment.location?.name || firstSegment.oord?.name || firstSegment.region?.name,
|
|
28793
|
+
(firstSegment.location?.name || firstSegment.oord?.name || firstSegment.region?.name) && firstSegment.country?.name
|
|
28794
|
+
? ', '
|
|
28795
|
+
: '',
|
|
28796
|
+
firstSegment.country?.name),
|
|
28797
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__segment-details-text" },
|
|
28798
|
+
dateFns.format(new Date(firstSegment.from), 'EEE. d MMM yyyy'),
|
|
28799
|
+
"> ",
|
|
28800
|
+
dateFns.format(new Date(firstSegment.to), 'EEE. d MMM yyyy'))))),
|
|
28801
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment" },
|
|
28802
|
+
React__default["default"].createElement("div", { className: `search__filter__itinerary__segment-item ${canEdit(firstSegment) ? 'search__filter__itinerary__segment-item--editable' : ''}`, onClick: () => {
|
|
28803
|
+
if (canEdit(firstSegment) && onEditAccommodation) {
|
|
28804
|
+
onEditAccommodation(group.segments);
|
|
28805
|
+
}
|
|
28806
|
+
} },
|
|
28807
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-date search__filter__itinerary__segment-nights" }, firstSegment.serviceType === ACCOMMODATION_SERVICE_TYPE && (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
28808
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__segment-date-date" },
|
|
28809
|
+
React__default["default"].createElement("strong", null, numberOfNights(firstSegment))),
|
|
28810
|
+
React__default["default"].createElement(Icon, { name: "ui-moon", width: 16, height: 16 })))),
|
|
28811
|
+
getSegmentIcon(firstSegment),
|
|
28812
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details" },
|
|
28813
|
+
React__default["default"].createElement("h6", null, getSegmentTitle(firstSegment)),
|
|
28814
|
+
firstSegment.serviceType === ACCOMMODATION_SERVICE_TYPE &&
|
|
28815
|
+
group.segments.map((segment, index) => (React__default["default"].createElement(React__default["default"].Fragment, { key: segment.guid },
|
|
28816
|
+
React__default["default"].createElement("strong", null,
|
|
28817
|
+
translations.SHARED.ROOM,
|
|
28818
|
+
"\u00A0",
|
|
28819
|
+
index + 1),
|
|
28820
|
+
segment.productName && segment.accommodationName && (React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details__room" },
|
|
28821
|
+
React__default["default"].createElement(Icon, { name: "ui-bed", width: 12, height: 12 }),
|
|
28822
|
+
segment.accommodationName)),
|
|
28823
|
+
segment.regimeName && (React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details__room" },
|
|
28824
|
+
React__default["default"].createElement(Icon, { name: "ui-utensils", width: 12, height: 12 }),
|
|
28825
|
+
segment.regimeName)))))))))));
|
|
28826
|
+
}),
|
|
28827
|
+
returnFlight && returnFlight !== outboundFlight && (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport" },
|
|
28828
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline" },
|
|
28829
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
|
|
28830
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-item" },
|
|
28831
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" },
|
|
28832
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-date-date" },
|
|
28833
|
+
React__default["default"].createElement("strong", null, dateFns.format(new Date(returnFlight.from), 'd'))),
|
|
28834
|
+
React__default["default"].createElement("p", null, dateFns.format(new Date(returnFlight.from), 'MMM'))),
|
|
28835
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-badge" },
|
|
28836
|
+
React__default["default"].createElement(Icon, { name: "ui-plane", height: 15 })),
|
|
28837
|
+
React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-details" },
|
|
28838
|
+
React__default["default"].createElement("h6", null, returnFlight.productName),
|
|
28839
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-plane" },
|
|
28840
|
+
React__default["default"].createElement("span", null,
|
|
28841
|
+
React__default["default"].createElement(Icon, { name: "ui-plane-depart", height: 14 }),
|
|
28842
|
+
" ",
|
|
28843
|
+
React__default["default"].createElement("strong", null, getDepartureTime(returnFlight))),
|
|
28844
|
+
' ',
|
|
28845
|
+
"-",
|
|
28846
|
+
' ',
|
|
28847
|
+
React__default["default"].createElement("span", null,
|
|
28848
|
+
React__default["default"].createElement(Icon, { name: "ui-plane-arrive", height: 14 }),
|
|
28849
|
+
" ",
|
|
28850
|
+
React__default["default"].createElement("strong", null, getArrivalTime(returnFlight)))),
|
|
28851
|
+
React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-time" },
|
|
28852
|
+
React__default["default"].createElement("span", null,
|
|
28853
|
+
React__default["default"].createElement(Icon, { name: "ui-clock", height: 20 }),
|
|
28854
|
+
" ",
|
|
28855
|
+
getDuration(returnFlight))))))))),
|
|
28066
28856
|
lastEntryLine && (React__default["default"].createElement("div", { className: "search__filter__itinerary__country" },
|
|
28067
28857
|
React__default["default"].createElement("div", { className: "search__filter__itinerary__country-icon" },
|
|
28068
28858
|
React__default["default"].createElement(Icon, { name: "ui-flag", width: 17.5, height: 20 })),
|
|
@@ -30433,10 +31223,9 @@ const getOptionPaxIds = (option) => {
|
|
|
30433
31223
|
const optionAppliesToPax = (option, paxId) => {
|
|
30434
31224
|
return getOptionPaxIds(option).includes(paxId);
|
|
30435
31225
|
};
|
|
30436
|
-
const optionAppliesToAllTravellers = (option,
|
|
31226
|
+
const optionAppliesToAllTravellers = (option, travellerIds) => {
|
|
30437
31227
|
const paxIds = getOptionPaxIds(option);
|
|
30438
|
-
|
|
30439
|
-
return paxIds.length === expected.length && paxIds.every((id, index) => id === expected[index]);
|
|
31228
|
+
return paxIds.length === travellerIds.length && travellerIds.every((id) => paxIds.includes(id));
|
|
30440
31229
|
};
|
|
30441
31230
|
const groupOptionsByExcursion = (options) => {
|
|
30442
31231
|
const groupedMap = new Map();
|
|
@@ -30461,26 +31250,26 @@ const ExcursionDetails = () => {
|
|
|
30461
31250
|
return null;
|
|
30462
31251
|
}
|
|
30463
31252
|
const translations = getTranslations(context.languageCode ?? 'en-GB');
|
|
30464
|
-
const
|
|
31253
|
+
const travellerIds = editablePackagingEntry.pax.map((pax) => pax.id);
|
|
30465
31254
|
const allOptions = React.useMemo(() => {
|
|
30466
31255
|
return selectedExcursionSearchResult.rooms.flatMap((room) => room.options ?? []);
|
|
30467
31256
|
}, [selectedExcursionSearchResult]);
|
|
30468
31257
|
const sharedOptions = React.useMemo(() => {
|
|
30469
|
-
return allOptions.filter((option) => optionAppliesToAllTravellers(option,
|
|
30470
|
-
}, [allOptions,
|
|
31258
|
+
return allOptions.filter((option) => optionAppliesToAllTravellers(option, travellerIds));
|
|
31259
|
+
}, [allOptions, travellerIds]);
|
|
30471
31260
|
const sharedExcursions = React.useMemo(() => {
|
|
30472
31261
|
return groupOptionsByExcursion(sharedOptions);
|
|
30473
31262
|
}, [sharedOptions]);
|
|
30474
31263
|
const paxGroups = React.useMemo(() => {
|
|
30475
31264
|
return editablePackagingEntry.pax.map((pax) => {
|
|
30476
|
-
const paxOptions = allOptions.filter((option) => optionAppliesToPax(option, pax.id) && !optionAppliesToAllTravellers(option,
|
|
31265
|
+
const paxOptions = allOptions.filter((option) => optionAppliesToPax(option, pax.id) && !optionAppliesToAllTravellers(option, travellerIds));
|
|
30477
31266
|
return {
|
|
30478
31267
|
pax,
|
|
30479
31268
|
paxId: pax.id,
|
|
30480
31269
|
excursions: groupOptionsByExcursion(paxOptions)
|
|
30481
31270
|
};
|
|
30482
31271
|
});
|
|
30483
|
-
}, [editablePackagingEntry.pax, allOptions,
|
|
31272
|
+
}, [editablePackagingEntry.pax, allOptions, travellerIds]);
|
|
30484
31273
|
const getSelectedSharedOption = () => {
|
|
30485
31274
|
return sharedOptions.find((option) => option.isSelected);
|
|
30486
31275
|
};
|
|
@@ -30488,11 +31277,11 @@ const ExcursionDetails = () => {
|
|
|
30488
31277
|
return sharedOptions.find((option) => option.accommodationCode === accommodationCode && option.isSelected);
|
|
30489
31278
|
};
|
|
30490
31279
|
const getSelectedOptionForPax = (paxId) => {
|
|
30491
|
-
return allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option,
|
|
31280
|
+
return allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerIds) && option.isSelected);
|
|
30492
31281
|
};
|
|
30493
31282
|
const getSelectedOptionForExcursion = (paxId, accommodationCode) => {
|
|
30494
31283
|
return allOptions.find((option) => optionAppliesToPax(option, paxId) &&
|
|
30495
|
-
!optionAppliesToAllTravellers(option,
|
|
31284
|
+
!optionAppliesToAllTravellers(option, travellerIds) &&
|
|
30496
31285
|
option.accommodationCode === accommodationCode &&
|
|
30497
31286
|
option.isSelected);
|
|
30498
31287
|
};
|
|
@@ -30502,7 +31291,7 @@ const ExcursionDetails = () => {
|
|
|
30502
31291
|
rooms: selectedExcursionSearchResult.rooms.map((room) => ({
|
|
30503
31292
|
...room,
|
|
30504
31293
|
options: room.options.map((option) => {
|
|
30505
|
-
const isSharedOption = optionAppliesToAllTravellers(option,
|
|
31294
|
+
const isSharedOption = optionAppliesToAllTravellers(option, travellerIds);
|
|
30506
31295
|
if (paxId === undefined) {
|
|
30507
31296
|
if (!isSharedOption) {
|
|
30508
31297
|
return option;
|
|
@@ -30549,7 +31338,7 @@ const ExcursionDetails = () => {
|
|
|
30549
31338
|
targetPrice = selectedOption.price;
|
|
30550
31339
|
}
|
|
30551
31340
|
else {
|
|
30552
|
-
const firstOption = allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option,
|
|
31341
|
+
const firstOption = allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerIds) && option.accommodationCode === accommodationCode);
|
|
30553
31342
|
targetPrice = firstOption?.price || 0;
|
|
30554
31343
|
}
|
|
30555
31344
|
return targetPrice - (currentSelectedPrice || 0);
|
|
@@ -30571,6 +31360,13 @@ const ExcursionDetails = () => {
|
|
|
30571
31360
|
return 'flyin__acco__price';
|
|
30572
31361
|
};
|
|
30573
31362
|
const handleConfirm = () => {
|
|
31363
|
+
if (context.trajectEntry) {
|
|
31364
|
+
const lines = buildTrajectExcursionLines(selectedExcursionSearchResult);
|
|
31365
|
+
if (lines.length)
|
|
31366
|
+
dispatch(addEditableEntryLines(lines));
|
|
31367
|
+
dispatch(setFlyInIsOpen(false));
|
|
31368
|
+
return;
|
|
31369
|
+
}
|
|
30574
31370
|
const dayKey = getExcursionDayKey(excursionSearchParams.date);
|
|
30575
31371
|
dispatch(confirmExcursionForDay({
|
|
30576
31372
|
dayKey,
|
|
@@ -30625,181 +31421,6 @@ const ExcursionDetails = () => {
|
|
|
30625
31421
|
React__default["default"].createElement("button", { type: "button", className: "cta cta--primary", onClick: handleConfirm }, translations?.QSM.CONFIRM))));
|
|
30626
31422
|
};
|
|
30627
31423
|
|
|
30628
|
-
const REGULAR_TRAJECT_NODE_TYPE = 0;
|
|
30629
|
-
const isSearchableTrajectNode = (node) => node.serviceType === ACCOMMODATION_SERVICE_TYPE || node.serviceType === EXCURSION_SERVICE_TYPE;
|
|
30630
|
-
const isTrajectFlightNode = (node) => node.serviceType === FLIGHT_SERVICE_TYPE;
|
|
30631
|
-
const getTrajectNodesWithLines = (trajectEntry) => {
|
|
30632
|
-
const linesByGuid = new Map((trajectEntry.entry.lines ?? []).map((line) => [line.guid, line]));
|
|
30633
|
-
return [...(trajectEntry.nodes ?? [])]
|
|
30634
|
-
.sort((a, b) => a.dayOrder - b.dayOrder || a.order - b.order)
|
|
30635
|
-
.map((node) => {
|
|
30636
|
-
const guids = node.lineGuids?.length ? node.lineGuids : [node.lineGuid];
|
|
30637
|
-
const lines = guids.map((guid) => linesByGuid.get(guid)).filter((x) => !!x);
|
|
30638
|
-
return { node, line: lines[0], lines };
|
|
30639
|
-
})
|
|
30640
|
-
.filter((x) => !!x.line);
|
|
30641
|
-
};
|
|
30642
|
-
const buildDestination = (line) => {
|
|
30643
|
-
if (line.location?.id)
|
|
30644
|
-
return { id: line.location.id, isLocation: true };
|
|
30645
|
-
if (line.oord?.id)
|
|
30646
|
-
return { id: line.oord.id, isOord: true };
|
|
30647
|
-
if (line.region?.id)
|
|
30648
|
-
return { id: line.region.id, isRegion: true };
|
|
30649
|
-
if (line.country?.id)
|
|
30650
|
-
return { id: line.country.id, isCountry: true };
|
|
30651
|
-
if (line.latitude && line.longitude)
|
|
30652
|
-
return { latitude: line.latitude, longitude: line.longitude };
|
|
30653
|
-
return { id: 0 };
|
|
30654
|
-
};
|
|
30655
|
-
const buildTrajectNodeRequest = ({ node, line }, context, productCode) => ({
|
|
30656
|
-
transactionId: context.transactionId,
|
|
30657
|
-
officeId: context.officeId,
|
|
30658
|
-
agentId: context.agentId ?? null,
|
|
30659
|
-
catalogueId: context.catalogueId,
|
|
30660
|
-
searchConfigurationId: context.searchConfigurationId,
|
|
30661
|
-
portalId: context.portalId ?? null,
|
|
30662
|
-
vendorConfigurationId: node.externalVendorId ?? null,
|
|
30663
|
-
language: context.language,
|
|
30664
|
-
serviceType: node.serviceType,
|
|
30665
|
-
fromDate: toDateOnlyString(line.from),
|
|
30666
|
-
toDate: toDateOnlyString(line.to),
|
|
30667
|
-
destination: buildDestination(line),
|
|
30668
|
-
productCode,
|
|
30669
|
-
rooms: context.rooms,
|
|
30670
|
-
tagIds: []
|
|
30671
|
-
});
|
|
30672
|
-
const getNodeCandidates = (node) => {
|
|
30673
|
-
const configured = (node.alternatives ?? []).filter((x) => !!x.code && x.code.trim().length > 0);
|
|
30674
|
-
if (configured.length)
|
|
30675
|
-
return configured;
|
|
30676
|
-
const fallbackCode = node.code ?? node.preferredAccommodationCode;
|
|
30677
|
-
if (!fallbackCode || !fallbackCode.trim().length)
|
|
30678
|
-
return [];
|
|
30679
|
-
return [
|
|
30680
|
-
{
|
|
30681
|
-
code: fallbackCode,
|
|
30682
|
-
name: node.name,
|
|
30683
|
-
description: node.description,
|
|
30684
|
-
imageUrl: node.imageUrl,
|
|
30685
|
-
contentSource: node.contentSource,
|
|
30686
|
-
vendor: node.vendor,
|
|
30687
|
-
externalVendorId: node.externalVendorId,
|
|
30688
|
-
isPreferred: true
|
|
30689
|
-
}
|
|
30690
|
-
];
|
|
30691
|
-
};
|
|
30692
|
-
const findCandidateForResult = (node, result) => getNodeCandidates(node).find((candidate) => matchesCode(result, candidate.code)) ?? null;
|
|
30693
|
-
const matchesCode = (result, code) => {
|
|
30694
|
-
if (result.code === code)
|
|
30695
|
-
return true;
|
|
30696
|
-
return (result.rooms ?? []).some((room) => (room.options ?? []).some((option) => option.accommodationCode === code));
|
|
30697
|
-
};
|
|
30698
|
-
const getPackagingRoomsFromEntry = (entry) => {
|
|
30699
|
-
const paxById = new Map((entry.pax ?? []).map((p) => [p.id, p]));
|
|
30700
|
-
const rooms = (entry.rooms ?? []).map((room) => ({
|
|
30701
|
-
travellers: (room.paxIds ?? []).map((paxId) => {
|
|
30702
|
-
const pax = paxById.get(paxId);
|
|
30703
|
-
return {
|
|
30704
|
-
id: paxId,
|
|
30705
|
-
age: pax?.age ?? null,
|
|
30706
|
-
dateOfBirth: pax?.dateOfBirth ?? null
|
|
30707
|
-
};
|
|
30708
|
-
})
|
|
30709
|
-
}));
|
|
30710
|
-
return rooms.filter((room) => room.travellers.length > 0);
|
|
30711
|
-
};
|
|
30712
|
-
const pickCheapest = (results) => [...results].sort((a, b) => a.price - b.price)[0] ?? null;
|
|
30713
|
-
const applyResultToLines = (lines, result) => lines.map((line, index) => applyResultToLine(line, result, index));
|
|
30714
|
-
const getOptionForRoom = (result, roomIndex) => {
|
|
30715
|
-
const room = (result.rooms ?? [])[roomIndex];
|
|
30716
|
-
if (room)
|
|
30717
|
-
return (room.options ?? []).find((x) => x.isSelected) ?? (room.options ?? [])[0];
|
|
30718
|
-
return (result.rooms ?? []).flatMap((x) => x.options ?? []).find((x) => x.isSelected) ?? (result.rooms ?? [])[0]?.options?.[0];
|
|
30719
|
-
};
|
|
30720
|
-
const applyResultToLine = (line, result, roomIndex = 0) => {
|
|
30721
|
-
const option = getOptionForRoom(result, roomIndex);
|
|
30722
|
-
return {
|
|
30723
|
-
...line,
|
|
30724
|
-
productName: result.name ?? line.productName,
|
|
30725
|
-
productCode: result.code ?? line.productCode,
|
|
30726
|
-
accommodationCode: option?.accommodationCode ?? line.accommodationCode,
|
|
30727
|
-
accommodationName: option?.accommodationName ?? line.accommodationName,
|
|
30728
|
-
regimeCode: option?.regimeCode ?? line.regimeCode,
|
|
30729
|
-
regimeName: option?.regimeName ?? line.regimeName,
|
|
30730
|
-
latitude: result.latitude ?? line.latitude,
|
|
30731
|
-
longitude: result.longitude ?? line.longitude,
|
|
30732
|
-
isChanged: true
|
|
30733
|
-
};
|
|
30734
|
-
};
|
|
30735
|
-
const toDateOnlyUtcString = (value) => {
|
|
30736
|
-
const date = new Date(value);
|
|
30737
|
-
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
|
|
30738
|
-
};
|
|
30739
|
-
const toTimeOnlyString = (value) => {
|
|
30740
|
-
const date = new Date(value);
|
|
30741
|
-
const hh = String(date.getUTCHours()).padStart(2, '0');
|
|
30742
|
-
const mm = String(date.getUTCMinutes()).padStart(2, '0');
|
|
30743
|
-
const ss = String(date.getUTCSeconds()).padStart(2, '0');
|
|
30744
|
-
return `${hh}:${mm}:${ss}`;
|
|
30745
|
-
};
|
|
30746
|
-
const mapFlightSegmentsToFlightLines = (segments) => segments.map((segment) => ({
|
|
30747
|
-
airlineCode: segment.marketingAirlineCode,
|
|
30748
|
-
airlineDescription: segment.marketingAirlineName,
|
|
30749
|
-
operatingAirlineCode: segment.operatingAirlineCode,
|
|
30750
|
-
operatingAirlineDescription: segment.operatingAirlineName,
|
|
30751
|
-
flightNumber: segment.flightNumber,
|
|
30752
|
-
operatingFlightNumber: segment.operatingFlightNumber ?? null,
|
|
30753
|
-
departureDate: toDateOnlyUtcString(segment.departureDateTime),
|
|
30754
|
-
departureTime: toTimeOnlyString(segment.departureDateTime),
|
|
30755
|
-
departureAirportCode: segment.departureAirportCode,
|
|
30756
|
-
departureAirportDescription: segment.departureAirportName,
|
|
30757
|
-
arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
|
|
30758
|
-
arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
|
|
30759
|
-
arrivalAirportCode: segment.arrivalAirportCode,
|
|
30760
|
-
arrivalAirportDescription: segment.arrivalAirportName,
|
|
30761
|
-
durationInTicks: segment.durationInTicks
|
|
30762
|
-
}));
|
|
30763
|
-
const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
|
|
30764
|
-
transactionId: context.transactionId,
|
|
30765
|
-
officeId: context.officeId,
|
|
30766
|
-
catalogueId: context.catalogueId,
|
|
30767
|
-
agentId: context.agentId ?? null,
|
|
30768
|
-
language: context.language,
|
|
30769
|
-
departureAirportCode: node.departureAirportCode ?? '',
|
|
30770
|
-
arrivalAirportCode: node.arrivalAirportCode ?? '',
|
|
30771
|
-
returnAirportCode: null,
|
|
30772
|
-
luggageIncluded: null,
|
|
30773
|
-
maxStops: null,
|
|
30774
|
-
travelClass: null,
|
|
30775
|
-
vendorConfigurationId: node.externalVendorId ?? null,
|
|
30776
|
-
pax,
|
|
30777
|
-
outward: { date: dateToDateStruct(new Date(line.from)) },
|
|
30778
|
-
return: null
|
|
30779
|
-
});
|
|
30780
|
-
const pickCheapestFlight = (flights) => [...flights].sort((a, b) => a.price - b.price)[0] ?? null;
|
|
30781
|
-
const applyFlightToLine = (line, flight) => {
|
|
30782
|
-
const segments = flight.outward?.segments ?? [];
|
|
30783
|
-
if (!segments.length)
|
|
30784
|
-
return line;
|
|
30785
|
-
const firstSegment = segments[0];
|
|
30786
|
-
const lastSegment = segments[segments.length - 1];
|
|
30787
|
-
return {
|
|
30788
|
-
...line,
|
|
30789
|
-
from: new Date(firstSegment.departureDateTime).toISOString(),
|
|
30790
|
-
to: new Date(lastSegment.arrivalDateTime).toISOString(),
|
|
30791
|
-
productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
|
|
30792
|
-
productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`,
|
|
30793
|
-
accommodationName: firstSegment.metaData?.farePriceClassName ?? line.accommodationName,
|
|
30794
|
-
accommodationCode: firstSegment.metaData?.fareCode ?? line.accommodationCode,
|
|
30795
|
-
flightInformation: {
|
|
30796
|
-
pnr: '',
|
|
30797
|
-
flightLines: mapFlightSegmentsToFlightLines(segments)
|
|
30798
|
-
},
|
|
30799
|
-
isChanged: true
|
|
30800
|
-
};
|
|
30801
|
-
};
|
|
30802
|
-
|
|
30803
31424
|
const getLocation = (result) => {
|
|
30804
31425
|
const place = result.locationName || result.regionName || result.oordName;
|
|
30805
31426
|
if (!place)
|
|
@@ -30814,13 +31435,12 @@ const toPlainText = (value) => {
|
|
|
30814
31435
|
.replace(/\s+/g, ' ')
|
|
30815
31436
|
.trim();
|
|
30816
31437
|
};
|
|
30817
|
-
const TrajectNodeCard = ({
|
|
30818
|
-
const { node } = item;
|
|
31438
|
+
const TrajectNodeCard = ({ node, result, isSelected, languageCode, translations, onSelect, onEditOptions, showNights, selectLabel, selectedLabel }) => {
|
|
30819
31439
|
const selectedPerRoom = (result.rooms ?? []).map((room) => (room.options ?? []).find((x) => x.isSelected) ?? (room.options ?? [])[0]);
|
|
30820
31440
|
const isMultiRoom = selectedPerRoom.length > 1;
|
|
30821
31441
|
const price = formatPrice$3(result.price, result.currencyCode, languageCode);
|
|
30822
31442
|
const nights = calculateNights(new Date(result.fromDate), new Date(result.toDate));
|
|
30823
|
-
const candidate = findCandidateForResult(node, result);
|
|
31443
|
+
const candidate = node ? findCandidateForResult(node, result) : null;
|
|
30824
31444
|
const image = candidate?.imageUrl ?? null;
|
|
30825
31445
|
const description = toPlainText(candidate?.description);
|
|
30826
31446
|
const title = candidate?.name ?? result.name;
|
|
@@ -30829,7 +31449,7 @@ const TrajectNodeCard = ({ item, result, isSelected, languageCode, translations,
|
|
|
30829
31449
|
React__default["default"].createElement("span", { className: "search__result-card__price" }, price)));
|
|
30830
31450
|
const canEditOptions = isSelected && !!onEditOptions && (result.rooms ?? []).some((room) => (room.options ?? []).length > 1);
|
|
30831
31451
|
const selectButton = (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
30832
|
-
React__default["default"].createElement("button", { type: "button", className: `cta ${isSelected ? 'cta--selected' : 'cta--select'}`, onClick: onSelect }, isSelected ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT),
|
|
31452
|
+
React__default["default"].createElement("button", { type: "button", className: `cta ${isSelected ? 'cta--selected' : 'cta--select'}`, onClick: onSelect }, isSelected ? selectedLabel ?? translations?.SHARED.SELECTED : selectLabel ?? translations?.SHARED.SELECT),
|
|
30833
31453
|
canEditOptions && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: onEditOptions },
|
|
30834
31454
|
translations?.SRP.SELECT,
|
|
30835
31455
|
" ",
|
|
@@ -30909,7 +31529,118 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
|
|
|
30909
31529
|
ordered.length,
|
|
30910
31530
|
"\u00A0",
|
|
30911
31531
|
translations.SRP.TOTAL_RESULTS_LABEL)),
|
|
30912
|
-
ordered.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards
|
|
31532
|
+
ordered.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards" }, ordered.map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, node: item.node, result: result, isSelected: result.code === availability?.selectedCode, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(result.code), showNights: item.node.serviceType === ACCOMMODATION_SERVICE_TYPE })))))));
|
|
31533
|
+
};
|
|
31534
|
+
|
|
31535
|
+
/**
|
|
31536
|
+
* Every flight found for one traject leg, shown in the fly-in.
|
|
31537
|
+
*
|
|
31538
|
+
* A one-way search can return far more options than belong in the results column, so the column
|
|
31539
|
+
* keeps three and hands the rest to this list — the same shape the regular flight results use.
|
|
31540
|
+
* Each leg is searched on its own, so the single leg of a result always sits in `outward`.
|
|
31541
|
+
*/
|
|
31542
|
+
const TrajectFlightsFlyIn = ({ isLoading }) => {
|
|
31543
|
+
const context = React.useContext(SearchResultsConfigurationContext);
|
|
31544
|
+
const dispatch = reactRedux.useDispatch();
|
|
31545
|
+
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
31546
|
+
const { trajectFlightAvailability, trajectEditLineGuid, flightsLoading } = reactRedux.useSelector((state) => state.searchResults);
|
|
31547
|
+
const trajectEntry = context?.trajectEntry;
|
|
31548
|
+
const item = React.useMemo(() => {
|
|
31549
|
+
if (!trajectEntry || !trajectEditLineGuid)
|
|
31550
|
+
return null;
|
|
31551
|
+
return getTrajectNodesWithLines(trajectEntry).find((x) => x.line.guid === trajectEditLineGuid) ?? null;
|
|
31552
|
+
}, [trajectEntry, trajectEditLineGuid]);
|
|
31553
|
+
if (!context || !item)
|
|
31554
|
+
return null;
|
|
31555
|
+
const availability = trajectFlightAvailability[item.line.guid];
|
|
31556
|
+
const flights = availability?.results ?? [];
|
|
31557
|
+
const selectedGuid = availability?.selectedGuid;
|
|
31558
|
+
const selected = flights.find((x) => x.outwardGuid === selectedGuid) ?? null;
|
|
31559
|
+
const handleSelect = (outwardGuid) => {
|
|
31560
|
+
const flight = flights.find((x) => x.outwardGuid === outwardGuid);
|
|
31561
|
+
if (!flight)
|
|
31562
|
+
return;
|
|
31563
|
+
dispatch(setTrajectFlightSelection({ lineGuid: item.line.guid, guid: flight.outwardGuid }));
|
|
31564
|
+
dispatch(updateEditableEntryLine(applyFlightToLine(item.line, flight)));
|
|
31565
|
+
dispatch(setTrajectEditLineGuid(null));
|
|
31566
|
+
dispatch(setFlyInIsOpen(false));
|
|
31567
|
+
};
|
|
31568
|
+
if (isLoading || flightsLoading) {
|
|
31569
|
+
return React__default["default"].createElement(React__default["default"].Fragment, null, context.customSpinner ?? React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_FLIGHTS }));
|
|
31570
|
+
}
|
|
31571
|
+
// The picked flight stays at the top; the rest follow by price.
|
|
31572
|
+
const ordered = [...flights].sort((a, b) => {
|
|
31573
|
+
if (a.outwardGuid === selectedGuid)
|
|
31574
|
+
return -1;
|
|
31575
|
+
if (b.outwardGuid === selectedGuid)
|
|
31576
|
+
return 1;
|
|
31577
|
+
return a.price - b.price;
|
|
31578
|
+
});
|
|
31579
|
+
return (React__default["default"].createElement("div", { className: "flyin__content" },
|
|
31580
|
+
React__default["default"].createElement("div", { className: "search__result-row" },
|
|
31581
|
+
React__default["default"].createElement("span", { className: "search__result-row-text" },
|
|
31582
|
+
ordered.length,
|
|
31583
|
+
"\u00A0",
|
|
31584
|
+
translations.SRP.TOTAL_RESULTS_LABEL)),
|
|
31585
|
+
ordered.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" }, ordered.map((result) => (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${result.outwardGuid}`, item: result.outward, guid: result.outwardGuid, selectedGuid: selectedGuid ?? undefined, isOutward: true, showSelectedState: true, price: result.price, currentSelectedPrice: selected?.price, onSelect: result.outwardGuid === selectedGuid ? undefined : () => handleSelect(result.outwardGuid) })))))));
|
|
31586
|
+
};
|
|
31587
|
+
|
|
31588
|
+
const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
|
|
31589
|
+
const context = React.useContext(SearchResultsConfigurationContext);
|
|
31590
|
+
const dispatch = reactRedux.useDispatch();
|
|
31591
|
+
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
31592
|
+
const { trajectNodeAvailability, trajectDaySuggestions, trajectActivitiesDayOrder, editablePackagingEntry } = reactRedux.useSelector((state) => state.searchResults);
|
|
31593
|
+
const trajectEntry = context?.trajectEntry;
|
|
31594
|
+
const items = React.useMemo(() => {
|
|
31595
|
+
if (!trajectEntry || trajectActivitiesDayOrder === null)
|
|
31596
|
+
return [];
|
|
31597
|
+
return getTrajectNodesWithLines(trajectEntry).filter(({ node }) => node.dayOrder === trajectActivitiesDayOrder && node.trajectNodeType === REGULAR_TRAJECT_NODE_TYPE && node.serviceType === EXCURSION_SERVICE_TYPE);
|
|
31598
|
+
}, [trajectEntry, trajectActivitiesDayOrder]);
|
|
31599
|
+
if (!context || trajectActivitiesDayOrder === null)
|
|
31600
|
+
return null;
|
|
31601
|
+
const suggestions = trajectDaySuggestions[trajectActivitiesDayOrder];
|
|
31602
|
+
const cards = buildDayActivityCards(items, trajectNodeAvailability, suggestions?.results ?? []);
|
|
31603
|
+
const getAddedLines = (result) => (editablePackagingEntry?.lines ?? []).filter((line) => line.serviceType === EXCURSION_SERVICE_TYPE && line.productCode === result.code && dateFns.isSameDay(new Date(line.from), new Date(result.fromDate)));
|
|
31604
|
+
/** Adding is not a decision that ends the list — the traveller may want several. */
|
|
31605
|
+
const handleToggle = (result) => {
|
|
31606
|
+
const added = getAddedLines(result);
|
|
31607
|
+
if (added.length) {
|
|
31608
|
+
dispatch(removeEditableEntryLines(added.map((line) => line.guid)));
|
|
31609
|
+
return;
|
|
31610
|
+
}
|
|
31611
|
+
if (excursionNeedsChoice(result)) {
|
|
31612
|
+
dispatch(setExcursionSearchParams(buildExcursionSearchParams(result)));
|
|
31613
|
+
dispatch(setSelectedExcursionSearchResult(result));
|
|
31614
|
+
dispatch(setFlyInType('excursion-details'));
|
|
31615
|
+
return;
|
|
31616
|
+
}
|
|
31617
|
+
const lines = buildTrajectExcursionLines(result);
|
|
31618
|
+
if (lines.length)
|
|
31619
|
+
dispatch(addEditableEntryLines(lines));
|
|
31620
|
+
};
|
|
31621
|
+
/** Swapping what the traject books is one decision, so the list closes behind it. */
|
|
31622
|
+
const handleSelect = ({ result, item }) => {
|
|
31623
|
+
if (!item)
|
|
31624
|
+
return;
|
|
31625
|
+
dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code: result.code }));
|
|
31626
|
+
applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
|
|
31627
|
+
dispatch(setTrajectActivitiesDayOrder(null));
|
|
31628
|
+
dispatch(setFlyInIsOpen(false));
|
|
31629
|
+
};
|
|
31630
|
+
if (isLoading || suggestions?.status === 'loading') {
|
|
31631
|
+
return React__default["default"].createElement(React__default["default"].Fragment, null, context.customSpinner ?? React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ACTIVITIES }));
|
|
31632
|
+
}
|
|
31633
|
+
return (React__default["default"].createElement("div", { className: "flyin__content" },
|
|
31634
|
+
React__default["default"].createElement("div", { className: "search__result-row" },
|
|
31635
|
+
React__default["default"].createElement("span", { className: "search__result-row-text" },
|
|
31636
|
+
cards.length,
|
|
31637
|
+
"\u00A0",
|
|
31638
|
+
translations.SRP.TOTAL_RESULTS_LABEL)),
|
|
31639
|
+
cards.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards" }, cards.map((card) => {
|
|
31640
|
+
const { result, item } = card;
|
|
31641
|
+
const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedLines(result).length > 0;
|
|
31642
|
+
return (React__default["default"].createElement(TrajectNodeCard, { key: `activity-${result.code}`, node: item?.node, result: result, isSelected: isSelected, languageCode: context.languageCode, translations: translations, onSelect: () => (item ? handleSelect(card) : handleToggle(result)), selectLabel: item ? undefined : translations.SRP.ADD, selectedLabel: item ? undefined : translations.SRP.REMOVE }));
|
|
31643
|
+
})))));
|
|
30913
31644
|
};
|
|
30914
31645
|
|
|
30915
31646
|
const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, detailsLoading, flyInType, isPackageEditFlow, handleConfirm, sortByTypes, activeSearchSeed, toggleFilters, filtersOpen }) => {
|
|
@@ -30984,8 +31715,8 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
|
|
|
30984
31715
|
dispatch(setBookPackagingEntry(true));
|
|
30985
31716
|
}
|
|
30986
31717
|
};
|
|
30987
|
-
return (React__default["default"].createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
|
|
30988
|
-
${flyInType === 'flight-outward-results' || flyInType === 'flight-return-results' ? 'flyin--flight' : ''}` },
|
|
31718
|
+
return (React__default["default"].createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' || flyInType === 'traject-day-activities' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
|
|
31719
|
+
${flyInType === 'flight-outward-results' || flyInType === 'flight-return-results' || flyInType === 'traject-flight-results' ? 'flyin--flight' : ''}` },
|
|
30989
31720
|
React__default["default"].createElement("div", { className: `flyin__panel ${isOpen ? 'flyin__panel--active' : ''}`, ref: panelRef },
|
|
30990
31721
|
React__default["default"].createElement("div", { className: "flyin__content" },
|
|
30991
31722
|
React__default["default"].createElement("div", { className: "flyin__content-title-row" },
|
|
@@ -31002,6 +31733,8 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
|
|
|
31002
31733
|
flyInType === 'flight-return-results' &&
|
|
31003
31734
|
`${translations.SRP.SELECT} ${translations.FLIGHTS_FORM.RETURN_FLIGHT}`,
|
|
31004
31735
|
srpType === build.PortalQsmType.AccommodationAndFlight && flyInType === 'flight-details' && 'Select your fare',
|
|
31736
|
+
flyInType === 'traject-flight-results' && `${translations.SRP.SELECT} ${translations.SRP.DEPARTURE}`,
|
|
31737
|
+
flyInType === 'traject-day-activities' && translations.SRP.ACTIVITIES,
|
|
31005
31738
|
flyInType === 'excursion-results' || (flyInType === 'excursion-details' && 'Select excursion')),
|
|
31006
31739
|
React__default["default"].createElement("span", { className: "flyin__close", onClick: () => handleClose() },
|
|
31007
31740
|
React__default["default"].createElement(Icon, { name: "ui-close", width: 30, height: 30, "aria-hidden": "true" }))),
|
|
@@ -31011,6 +31744,8 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
|
|
|
31011
31744
|
"Go Back")))),
|
|
31012
31745
|
srpType === build.PortalQsmType.Flight && React__default["default"].createElement(FlightsFlyIn, { isOpen: isOpen, setIsOpen: setIsOpen }),
|
|
31013
31746
|
context?.trajectEntry && flyInType === 'acco-results' && React__default["default"].createElement(TrajectResultsFlyIn, { isLoading: detailsLoading }),
|
|
31747
|
+
context?.trajectEntry && flyInType === 'traject-flight-results' && React__default["default"].createElement(TrajectFlightsFlyIn, { isLoading: detailsLoading }),
|
|
31748
|
+
context?.trajectEntry && flyInType === 'traject-day-activities' && React__default["default"].createElement(TrajectDayActivitiesFlyIn, { isLoading: detailsLoading }),
|
|
31014
31749
|
!context?.trajectEntry &&
|
|
31015
31750
|
(srpType === build.PortalQsmType.Accommodation || srpType === build.PortalQsmType.AccommodationAndFlight) &&
|
|
31016
31751
|
flyInType === 'acco-results' && (React__default["default"].createElement("div", { className: "flyin__content flyin__content--columns" },
|
|
@@ -32841,6 +33576,27 @@ const DayByDayExcursions = () => {
|
|
|
32841
33576
|
})));
|
|
32842
33577
|
};
|
|
32843
33578
|
|
|
33579
|
+
/**
|
|
33580
|
+
* The entry to send to the backend, as opposed to the one the page works with.
|
|
33581
|
+
*
|
|
33582
|
+
* They differ only for a traject: the page keeps the line guids the traject was rendered with,
|
|
33583
|
+
* because that is what joins its nodes to its lines, while pricing and booking need the guids the
|
|
33584
|
+
* searches cached their lines under. Anything else passes through untouched, so this can be used
|
|
33585
|
+
* wherever the entry leaves the component without checking which flow is running.
|
|
33586
|
+
*/
|
|
33587
|
+
const useBookableEntry = () => {
|
|
33588
|
+
const context = React.useContext(SearchResultsConfigurationContext);
|
|
33589
|
+
const { editablePackagingEntry, trajectNodeAvailability, trajectFlightAvailability } = reactRedux.useSelector((state) => state.searchResults);
|
|
33590
|
+
const trajectEntry = context?.trajectEntry;
|
|
33591
|
+
return React.useMemo(() => {
|
|
33592
|
+
if (!editablePackagingEntry)
|
|
33593
|
+
return null;
|
|
33594
|
+
if (!trajectEntry)
|
|
33595
|
+
return editablePackagingEntry;
|
|
33596
|
+
return withCachedEntryLineGuids(editablePackagingEntry, trajectEntry, trajectNodeAvailability, trajectFlightAvailability);
|
|
33597
|
+
}, [editablePackagingEntry, trajectEntry, trajectNodeAvailability, trajectFlightAvailability]);
|
|
33598
|
+
};
|
|
33599
|
+
|
|
32844
33600
|
const renderEditablePackagingEntrySummaryOptions = (editablePackagingEntry, priceDetails, translations, currencyCode) => {
|
|
32845
33601
|
const priceDetailsByProduct = lodash.groupBy(priceDetails?.details ?? [], (detail) => `${detail.productCode}|${detail.accommodationCode}`);
|
|
32846
33602
|
return (editablePackagingEntry?.lines ?? []).map((line) => {
|
|
@@ -33225,6 +33981,8 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
|
|
|
33225
33981
|
setSelectedPackagingAccoResult(selectedPackagingAccoResult);
|
|
33226
33982
|
}
|
|
33227
33983
|
}, [selectedPackagingAccoResultCode, packagingAccoResults]);
|
|
33984
|
+
// What the page holds versus what the backend can look up; they differ for a traject.
|
|
33985
|
+
const bookableEntry = useBookableEntry();
|
|
33228
33986
|
if (!context || !editablePackagingEntry || !priceDetails || !config)
|
|
33229
33987
|
return React__default["default"].createElement(Spinner, { label: translations.SUMMARY.PROCESS_BOOKING });
|
|
33230
33988
|
const handleSummarySubmit = async (e) => {
|
|
@@ -33233,20 +33991,15 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
|
|
|
33233
33991
|
if (typeof window !== 'undefined') {
|
|
33234
33992
|
window.scrollTo(0, 0);
|
|
33235
33993
|
}
|
|
33236
|
-
|
|
33237
|
-
|
|
33238
|
-
remarks
|
|
33239
|
-
};
|
|
33994
|
+
// Held apart from the entry itself so they can be put on both copies of it.
|
|
33995
|
+
let bookingFields = { remarks };
|
|
33240
33996
|
if (context.generatePaymentUrl && typeof window !== 'undefined') {
|
|
33241
33997
|
const redirectUrl = new URL(window.location.href);
|
|
33242
33998
|
redirectUrl.searchParams.set('bookingConfirmation', 'true');
|
|
33243
33999
|
redirectUrl.searchParams.set('link', '');
|
|
33244
|
-
|
|
33245
|
-
...updatedEditablePackagingEntry,
|
|
33246
|
-
redirectUrl: redirectUrl.toString(),
|
|
33247
|
-
returnPaymentUrl: true
|
|
33248
|
-
};
|
|
34000
|
+
bookingFields = { ...bookingFields, redirectUrl: redirectUrl.toString(), returnPaymentUrl: true };
|
|
33249
34001
|
}
|
|
34002
|
+
const updatedEditablePackagingEntry = { ...editablePackagingEntry, ...bookingFields };
|
|
33250
34003
|
dispatch(setEditablePackagingEntry(updatedEditablePackagingEntry));
|
|
33251
34004
|
try {
|
|
33252
34005
|
const request = {
|
|
@@ -33254,7 +34007,8 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
|
|
|
33254
34007
|
officeId: context.tideConnection.officeId,
|
|
33255
34008
|
catalogueId: context.searchConfiguration.defaultCatalogueId ?? 0,
|
|
33256
34009
|
agentId: context.agentId,
|
|
33257
|
-
|
|
34010
|
+
// The page keeps the entry it works with; the backend gets the one it can look up.
|
|
34011
|
+
payload: { ...(bookableEntry ?? editablePackagingEntry), ...bookingFields }
|
|
33258
34012
|
};
|
|
33259
34013
|
const bookingResponse = await build.bookPackagingEntry(config, request);
|
|
33260
34014
|
dispatch(setBookingNumber(bookingResponse.number));
|
|
@@ -33286,6 +34040,41 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
|
|
|
33286
34040
|
React__default["default"].createElement(WLSidebar, { activeSearchSeed: activeSearchSeed, packagingAccoResult: selectedPackagingAccoResult }))));
|
|
33287
34041
|
};
|
|
33288
34042
|
|
|
34043
|
+
const useTrajectDayActivities = () => {
|
|
34044
|
+
const context = React.useContext(SearchResultsConfigurationContext);
|
|
34045
|
+
const dispatch = reactRedux.useDispatch();
|
|
34046
|
+
const { trajectDaySuggestions, trajectNodeAvailability } = reactRedux.useSelector((state) => state.searchResults);
|
|
34047
|
+
const trajectEntry = context?.trajectEntry;
|
|
34048
|
+
const showApiItems = !!context?.showApiItems;
|
|
34049
|
+
const loadDay = React.useCallback(async (day) => {
|
|
34050
|
+
if (!context || !trajectEntry)
|
|
34051
|
+
return;
|
|
34052
|
+
// Already asked for, or already answered: leave it be.
|
|
34053
|
+
if (trajectDaySuggestions[day.order])
|
|
34054
|
+
return;
|
|
34055
|
+
const nodesWithLines = getTrajectNodesWithLines(trajectEntry);
|
|
34056
|
+
const destination = getDayDestination(nodesWithLines, trajectNodeAvailability, day.order);
|
|
34057
|
+
if (!destination)
|
|
34058
|
+
return;
|
|
34059
|
+
dispatch(setTrajectDaySuggestionsLoading(day.order));
|
|
34060
|
+
try {
|
|
34061
|
+
const results = (await build.searchPackagingExcursions(buildTrajectClientConfig(context), buildTrajectDayExcursionRequest(day.date, destination, buildTrajectSearchContext(context, trajectEntry)))) ?? [];
|
|
34062
|
+
// With external items switched off a day only offers what Tide holds itself, and whatever
|
|
34063
|
+
// the traject already books that day has a card of its own further up the block.
|
|
34064
|
+
const configured = getConfiguredExcursionCodes(nodesWithLines, day.order);
|
|
34065
|
+
dispatch(setTrajectDaySuggestions({
|
|
34066
|
+
dayOrder: day.order,
|
|
34067
|
+
results: results.filter((x) => showApiItems || !isExternalResult(x)).filter((x) => !configured.has(x.code))
|
|
34068
|
+
}));
|
|
34069
|
+
}
|
|
34070
|
+
catch (error) {
|
|
34071
|
+
console.error('Failed to load traject day activities', day.order, error);
|
|
34072
|
+
dispatch(setTrajectDaySuggestions({ dayOrder: day.order, results: [] }));
|
|
34073
|
+
}
|
|
34074
|
+
}, [context, trajectEntry, showApiItems, trajectDaySuggestions, trajectNodeAvailability, dispatch]);
|
|
34075
|
+
return loadDay;
|
|
34076
|
+
};
|
|
34077
|
+
|
|
33289
34078
|
const getNodeIcon = (node) => {
|
|
33290
34079
|
if (isTrajectFlightNode(node))
|
|
33291
34080
|
return 'ui-flight';
|
|
@@ -33295,14 +34084,18 @@ const getNodeIcon = (node) => {
|
|
|
33295
34084
|
return 'ui-bed';
|
|
33296
34085
|
return 'ui-location';
|
|
33297
34086
|
};
|
|
34087
|
+
const isRenderableNode = (node) => node.trajectNodeType === REGULAR_TRAJECT_NODE_TYPE &&
|
|
34088
|
+
(node.serviceType === ACCOMMODATION_SERVICE_TYPE || node.serviceType === EXCURSION_SERVICE_TYPE || node.serviceType === FLIGHT_SERVICE_TYPE);
|
|
34089
|
+
const MAX_DAY_ACTIVITIES = 6;
|
|
33298
34090
|
const TrajectResults = ({ isLoading }) => {
|
|
33299
34091
|
const context = React.useContext(SearchResultsConfigurationContext);
|
|
33300
34092
|
const dispatch = reactRedux.useDispatch();
|
|
34093
|
+
const loadDay = useTrajectDayActivities();
|
|
33301
34094
|
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
33302
34095
|
const locale = getLocale(context?.languageCode ?? 'en-GB');
|
|
33303
|
-
const { trajectNodeAvailability, trajectFlightAvailability } = reactRedux.useSelector((state) => state.searchResults);
|
|
33304
|
-
const [expandedNodes, setExpandedNodes] = React.useState({});
|
|
34096
|
+
const { trajectNodeAvailability, trajectFlightAvailability, trajectDaySuggestions, editablePackagingEntry, trajectResolving } = reactRedux.useSelector((state) => state.searchResults);
|
|
33305
34097
|
const trajectEntry = context?.trajectEntry;
|
|
34098
|
+
const hideShowMoreResults = !!context?.hideShowMoreResults;
|
|
33306
34099
|
const nodesWithLines = React.useMemo(() => (trajectEntry ? getTrajectNodesWithLines(trajectEntry) : []), [trajectEntry]);
|
|
33307
34100
|
const lineByNodeId = React.useMemo(() => new Map(nodesWithLines.map((x) => [x.node.nodeId, x])), [nodesWithLines]);
|
|
33308
34101
|
const nodesByDay = React.useMemo(() => {
|
|
@@ -33316,9 +34109,22 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33316
34109
|
});
|
|
33317
34110
|
return map;
|
|
33318
34111
|
}, [trajectEntry]);
|
|
34112
|
+
const { stayByDay, stayStartByNodeId } = React.useMemo(() => {
|
|
34113
|
+
const byDay = new Map();
|
|
34114
|
+
const byNodeId = new Map();
|
|
34115
|
+
nodesWithLines
|
|
34116
|
+
.filter((x) => x.node.serviceType === ACCOMMODATION_SERVICE_TYPE)
|
|
34117
|
+
.forEach((item) => {
|
|
34118
|
+
const nights = Math.max(dateFns.differenceInCalendarDays(new Date(item.line.to), new Date(item.line.from)), 1);
|
|
34119
|
+
byNodeId.set(item.node.nodeId, { item, night: 0, nights });
|
|
34120
|
+
for (let night = 0; night < nights; night++) {
|
|
34121
|
+
byDay.set(item.node.dayOrder + night, { item, night, nights });
|
|
34122
|
+
}
|
|
34123
|
+
});
|
|
34124
|
+
return { stayByDay: byDay, stayStartByNodeId: byNodeId };
|
|
34125
|
+
}, [nodesWithLines]);
|
|
33319
34126
|
if (!context || !trajectEntry)
|
|
33320
34127
|
return null;
|
|
33321
|
-
const toggleExpanded = (lineGuid) => setExpandedNodes((prev) => ({ ...prev, [lineGuid]: !prev[lineGuid] }));
|
|
33322
34128
|
const handleSelect = (item, code) => {
|
|
33323
34129
|
const availability = trajectNodeAvailability[item.line.guid];
|
|
33324
34130
|
const result = availability?.results.find((x) => x.code === code);
|
|
@@ -33327,6 +34133,12 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33327
34133
|
dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
|
|
33328
34134
|
applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
|
|
33329
34135
|
};
|
|
34136
|
+
/** The remaining legs for a flight node open in the fly-in, as the regular flight results do. */
|
|
34137
|
+
const handleShowMoreFlights = (item) => {
|
|
34138
|
+
dispatch(setTrajectEditLineGuid(item.line.guid));
|
|
34139
|
+
dispatch(setFlyInType('traject-flight-results'));
|
|
34140
|
+
dispatch(setFlyInIsOpen(true));
|
|
34141
|
+
};
|
|
33330
34142
|
const handleShowMore = (item) => {
|
|
33331
34143
|
dispatch(setTrajectEditLineGuid(item.line.guid));
|
|
33332
34144
|
dispatch(setFlyInType('acco-results'));
|
|
@@ -33343,8 +34155,41 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33343
34155
|
dispatch(setFlyInType('acco-details'));
|
|
33344
34156
|
dispatch(setFlyInIsOpen(true));
|
|
33345
34157
|
};
|
|
33346
|
-
const
|
|
33347
|
-
|
|
34158
|
+
const getStayLocation = (item) => {
|
|
34159
|
+
const availability = trajectNodeAvailability[item.line.guid];
|
|
34160
|
+
const result = availability?.results.find((x) => x.code === availability?.selectedCode);
|
|
34161
|
+
return (result?.locationName ||
|
|
34162
|
+
result?.oordName ||
|
|
34163
|
+
result?.regionName ||
|
|
34164
|
+
result?.countryName ||
|
|
34165
|
+
item.line.location?.name ||
|
|
34166
|
+
item.line.oord?.name ||
|
|
34167
|
+
item.line.region?.name ||
|
|
34168
|
+
item.line.country?.name ||
|
|
34169
|
+
item.node.alternatives?.find((x) => x.isPreferred)?.city ||
|
|
34170
|
+
'');
|
|
34171
|
+
};
|
|
34172
|
+
const getStayName = (item) => {
|
|
34173
|
+
const availability = trajectNodeAvailability[item.line.guid];
|
|
34174
|
+
const result = availability?.results.find((x) => x.code === availability?.selectedCode);
|
|
34175
|
+
return result?.name || item.node.preferredAccommodationName || item.node.name || '';
|
|
34176
|
+
};
|
|
34177
|
+
const getStayTitle = (stay) => [getStayLocation(stay.item), getStayName(stay.item), `${stay.night + 1} ${translations.SRP.OF} ${stay.nights} ${translations.SRP.NIGHTS}`]
|
|
34178
|
+
.filter(Boolean)
|
|
34179
|
+
.join(' - ');
|
|
34180
|
+
const renderDayHeader = (day, stay) => {
|
|
34181
|
+
const date = new Date(day.date);
|
|
34182
|
+
const title = stay ? getStayTitle(stay) : day.name;
|
|
34183
|
+
return (React__default["default"].createElement("div", { className: "search__results__label search__results__label--secondary" },
|
|
34184
|
+
React__default["default"].createElement("div", { className: "search__results__label__date" },
|
|
34185
|
+
React__default["default"].createElement("p", { className: "search__results__label__date-date" }, dateFns.format(date, 'd', { locale })),
|
|
34186
|
+
React__default["default"].createElement("p", null, dateFns.format(date, 'MMM', { locale }))),
|
|
34187
|
+
React__default["default"].createElement("div", { className: "search__results__label__text" },
|
|
34188
|
+
React__default["default"].createElement(Icon, { name: stay ? 'ui-bed' : 'ui-location', height: 16 }),
|
|
34189
|
+
React__default["default"].createElement("h3", null,
|
|
34190
|
+
React__default["default"].createElement("strong", null, title)))));
|
|
34191
|
+
};
|
|
34192
|
+
const renderServiceNode = (item, showHeader = true, headerTitle) => {
|
|
33348
34193
|
const { node, line } = item;
|
|
33349
34194
|
const availability = trajectNodeAvailability[line.guid];
|
|
33350
34195
|
const results = availability?.results ?? [];
|
|
@@ -33354,13 +34199,15 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33354
34199
|
// No entry yet means the node's search has been queued but not dispatched — still loading.
|
|
33355
34200
|
const status = availability?.status ?? 'loading';
|
|
33356
34201
|
return (React__default["default"].createElement(React__default["default"].Fragment, { key: line.guid },
|
|
33357
|
-
React__default["default"].createElement("div", { className: "search__results__label search__results__label--secondary" },
|
|
34202
|
+
showHeader && (React__default["default"].createElement("div", { className: "search__results__label search__results__label--secondary" },
|
|
33358
34203
|
React__default["default"].createElement("div", { className: "search__results__label__date" },
|
|
33359
34204
|
React__default["default"].createElement("p", { className: "search__results__label__date-date" }, dateFns.format(new Date(line.from), 'd', { locale })),
|
|
33360
34205
|
React__default["default"].createElement("p", null, dateFns.format(new Date(line.from), 'MMM', { locale }))),
|
|
33361
34206
|
React__default["default"].createElement("div", { className: "search__results__label__text" },
|
|
33362
|
-
React__default["default"].createElement(Icon, { name: getNodeIcon(node), height: 16 })
|
|
33363
|
-
|
|
34207
|
+
React__default["default"].createElement(Icon, { name: getNodeIcon(node), height: 16 }),
|
|
34208
|
+
React__default["default"].createElement("h3", null,
|
|
34209
|
+
React__default["default"].createElement("strong", null, headerTitle || node.name))))),
|
|
34210
|
+
status === 'loading' && React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ACCOMMODATIONS }),
|
|
33364
34211
|
status === 'unavailable' && React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
|
|
33365
34212
|
status === 'resolved' && selected && (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
33366
34213
|
availability.preferredUnavailable && node.preferredAccommodationName && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
@@ -33368,8 +34215,8 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33368
34215
|
node.preferredAccommodationName,
|
|
33369
34216
|
" \u2014 ",
|
|
33370
34217
|
translations.SRP.NO_RESULTS))),
|
|
33371
|
-
React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--
|
|
33372
|
-
others.length > 0 && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
34218
|
+
React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--scroll" }, [selected, ...configured].map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, node: node, result: result, isSelected: result.code === selected.code, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(item, result.code), onEditOptions: node.serviceType === ACCOMMODATION_SERVICE_TYPE ? () => handleEditOptions(item) : undefined, showNights: node.serviceType === ACCOMMODATION_SERVICE_TYPE })))),
|
|
34219
|
+
others.length > 0 && !hideShowMoreResults && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
33373
34220
|
React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMore(item) },
|
|
33374
34221
|
translations.SRP.SHOW_MORE,
|
|
33375
34222
|
" (",
|
|
@@ -33387,8 +34234,7 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33387
34234
|
const flights = availability?.results ?? [];
|
|
33388
34235
|
const selected = flights.find((x) => x.outwardGuid === availability?.selectedGuid) ?? null;
|
|
33389
34236
|
const others = flights.filter((x) => x.outwardGuid !== availability?.selectedGuid);
|
|
33390
|
-
const
|
|
33391
|
-
const visible = isExpanded ? others : others.slice(0, 2);
|
|
34237
|
+
const visible = others.slice(0, 2);
|
|
33392
34238
|
return (React__default["default"].createElement(React__default["default"].Fragment, { key: line.guid },
|
|
33393
34239
|
React__default["default"].createElement("div", { className: "search__results__label search__results__label--secondary" },
|
|
33394
34240
|
React__default["default"].createElement("div", { className: "search__results__label__date" },
|
|
@@ -33397,8 +34243,6 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33397
34243
|
React__default["default"].createElement("div", { className: "search__results__label__text" },
|
|
33398
34244
|
React__default["default"].createElement(Icon, { name: "ui-flight", height: 16 }),
|
|
33399
34245
|
React__default["default"].createElement("h3", null,
|
|
33400
|
-
translations.SRP.SELECT,
|
|
33401
|
-
' ',
|
|
33402
34246
|
React__default["default"].createElement("strong", null,
|
|
33403
34247
|
node.departureAirportCode,
|
|
33404
34248
|
" - ",
|
|
@@ -33409,41 +34253,139 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
33409
34253
|
React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" },
|
|
33410
34254
|
React__default["default"].createElement(IndependentFlightOption, { key: `flight-${selected.outwardGuid}`, item: selected.outward, guid: selected.outwardGuid, selectedGuid: selected.outwardGuid, isOutward: true, showSelectedState: true, price: selected.price }),
|
|
33411
34255
|
visible.map((result) => (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${result.outwardGuid}`, item: result.outward, guid: result.outwardGuid, isOutward: true, price: result.price, currentSelectedPrice: selected.price, onSelect: () => handleSelectFlight(item, result) })))),
|
|
33412
|
-
others.length > 2 && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
33413
|
-
React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () =>
|
|
34256
|
+
others.length > 2 && !hideShowMoreResults && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
34257
|
+
React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreFlights(item) },
|
|
34258
|
+
translations.SRP.SHOW_MORE,
|
|
34259
|
+
" (",
|
|
34260
|
+
others.length - 2,
|
|
34261
|
+
")")))))));
|
|
34262
|
+
};
|
|
34263
|
+
/** The lines a suggested activity put on the entry, which is also how we know it was added. */
|
|
34264
|
+
const getAddedExcursionLines = (result) => (editablePackagingEntry?.lines ?? []).filter((line) => line.serviceType === EXCURSION_SERVICE_TYPE && line.productCode === result.code && dateFns.isSameDay(new Date(line.from), new Date(result.fromDate)));
|
|
34265
|
+
const handleToggleActivity = (result) => {
|
|
34266
|
+
const added = getAddedExcursionLines(result);
|
|
34267
|
+
if (added.length) {
|
|
34268
|
+
dispatch(removeEditableEntryLines(added.map((line) => line.guid)));
|
|
34269
|
+
return;
|
|
34270
|
+
}
|
|
34271
|
+
if (excursionNeedsChoice(result)) {
|
|
34272
|
+
dispatch(setExcursionSearchParams(buildExcursionSearchParams(result)));
|
|
34273
|
+
dispatch(setSelectedExcursionSearchResult(result));
|
|
34274
|
+
dispatch(setFlyInType('excursion-details'));
|
|
34275
|
+
dispatch(setFlyInIsOpen(true));
|
|
34276
|
+
return;
|
|
34277
|
+
}
|
|
34278
|
+
const lines = buildTrajectExcursionLines(result);
|
|
34279
|
+
if (lines.length)
|
|
34280
|
+
dispatch(addEditableEntryLines(lines));
|
|
34281
|
+
};
|
|
34282
|
+
const handleShowItinerary = () => {
|
|
34283
|
+
dispatch(setTrajectItineraryView(true));
|
|
34284
|
+
if (typeof window !== 'undefined')
|
|
34285
|
+
window.scrollTo(0, 0);
|
|
34286
|
+
};
|
|
34287
|
+
const handleShowMoreActivities = (dayOrder) => {
|
|
34288
|
+
dispatch(setTrajectActivitiesDayOrder(dayOrder));
|
|
34289
|
+
dispatch(setFlyInType('traject-day-activities'));
|
|
34290
|
+
dispatch(setFlyInIsOpen(true));
|
|
34291
|
+
};
|
|
34292
|
+
/** The excursion nodes the traject itself puts on a day. */
|
|
34293
|
+
const getDayExcursionItems = (dayOrder) => (nodesByDay.get(dayOrder) ?? [])
|
|
34294
|
+
.filter((node) => node.trajectNodeType === REGULAR_TRAJECT_NODE_TYPE && node.serviceType === EXCURSION_SERVICE_TYPE)
|
|
34295
|
+
.map((node) => lineByNodeId.get(node.nodeId))
|
|
34296
|
+
.filter((x) => !!x);
|
|
34297
|
+
/**
|
|
34298
|
+
* One block per day for everything there is to do that day.
|
|
34299
|
+
*
|
|
34300
|
+
* What the traject books sits first and stays selected; the rest of the day follows as things
|
|
34301
|
+
* the traveller may add. They share a row because they answer the same question — an excursion
|
|
34302
|
+
* on the traject and one found for the day are alternatives to each other, and a block apiece
|
|
34303
|
+
* would only hide that.
|
|
34304
|
+
*/
|
|
34305
|
+
const renderDayActivities = (day) => {
|
|
34306
|
+
const items = getDayExcursionItems(day.order);
|
|
34307
|
+
const suggestions = trajectDaySuggestions[day.order];
|
|
34308
|
+
const cards = buildDayActivityCards(items, trajectNodeAvailability, suggestions?.results ?? []);
|
|
34309
|
+
const nodesResolving = items.some((item) => (trajectNodeAvailability[item.line.guid]?.status ?? 'loading') === 'loading');
|
|
34310
|
+
const isSearching = suggestions?.status === 'loading';
|
|
34311
|
+
const canSearch = !!getDayDestination(nodesWithLines, trajectNodeAvailability, day.order);
|
|
34312
|
+
if (!items.length && !canSearch)
|
|
34313
|
+
return null;
|
|
34314
|
+
const visible = cards.slice(0, MAX_DAY_ACTIVITIES);
|
|
34315
|
+
const hidden = cards.length - visible.length;
|
|
34316
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, { key: `activities-${day.order}` },
|
|
34317
|
+
React__default["default"].createElement("div", { className: "search__results__label search__results__label--secondary" },
|
|
34318
|
+
React__default["default"].createElement("div", { className: "search__results__label__date" },
|
|
34319
|
+
React__default["default"].createElement("p", { className: "search__results__label__date-date" }, dateFns.format(new Date(day.date), 'd', { locale })),
|
|
34320
|
+
React__default["default"].createElement("p", null, dateFns.format(new Date(day.date), 'MMM', { locale }))),
|
|
34321
|
+
React__default["default"].createElement("div", { className: "search__results__label__text" },
|
|
34322
|
+
React__default["default"].createElement(Icon, { name: "ui-excursion", height: 16 }),
|
|
34323
|
+
React__default["default"].createElement("h3", null,
|
|
34324
|
+
React__default["default"].createElement("strong", null, translations.SRP.ACTIVITIES)))),
|
|
34325
|
+
(nodesResolving || isSearching) && !cards.length && React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ACTIVITIES }),
|
|
34326
|
+
!!cards.length && React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--scroll" }, visible.map(renderActivityCard)),
|
|
34327
|
+
!nodesResolving && !isSearching && !cards.length && (!!items.length || !!suggestions) && (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)),
|
|
34328
|
+
React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
34329
|
+
!suggestions && canSearch && !cards.length && !hideShowMoreResults && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => loadDay(day) }, translations.SRP.SHOW_ACTIVITIES)),
|
|
34330
|
+
hidden > 0 && !hideShowMoreResults && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreActivities(day.order) },
|
|
34331
|
+
translations.SRP.SHOW_MORE,
|
|
34332
|
+
" (",
|
|
34333
|
+
hidden,
|
|
34334
|
+
")")))));
|
|
34335
|
+
};
|
|
34336
|
+
/**
|
|
34337
|
+
* An activity the traject books is swapped for another, the way a hotel is; one the traveller
|
|
34338
|
+
* found for themselves is added to the trip or taken back off it.
|
|
34339
|
+
*/
|
|
34340
|
+
const renderActivityCard = ({ result, item }) => {
|
|
34341
|
+
const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedExcursionLines(result).length > 0;
|
|
34342
|
+
return (React__default["default"].createElement(TrajectNodeCard, { key: `activity-${result.code}`, node: item?.node, result: result, isSelected: isSelected, languageCode: context.languageCode, translations: translations, onSelect: () => (item ? handleSelect(item, result.code) : handleToggleActivity(result)), selectLabel: item ? undefined : translations.SRP.ADD, selectedLabel: item ? undefined : translations.SRP.REMOVE }));
|
|
33414
34343
|
};
|
|
33415
34344
|
if (isLoading)
|
|
33416
34345
|
return React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ITINERARY });
|
|
33417
|
-
return (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
33418
|
-
|
|
33419
|
-
|
|
33420
|
-
|
|
33421
|
-
|
|
33422
|
-
|
|
33423
|
-
|
|
33424
|
-
|
|
33425
|
-
|
|
33426
|
-
|
|
33427
|
-
|
|
33428
|
-
|
|
33429
|
-
|
|
33430
|
-
|
|
33431
|
-
|
|
33432
|
-
|
|
33433
|
-
|
|
33434
|
-
|
|
34346
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
34347
|
+
trajectEntry.traject.days.map((day) => {
|
|
34348
|
+
const stay = stayByDay.get(day.order);
|
|
34349
|
+
const dayNodes = (nodesByDay.get(day.order) ?? []).filter((node) => isRenderableNode(node) && lineByNodeId.has(node.nodeId));
|
|
34350
|
+
const stayLeadsDay = !!stay && (stay.night > 0 || dayNodes[0]?.nodeId === stay.item.node.nodeId);
|
|
34351
|
+
// Where the day's activities belong in the order: with the first excursion it carries.
|
|
34352
|
+
const firstExcursionNodeId = dayNodes.find((node) => node.serviceType === EXCURSION_SERVICE_TYPE)?.nodeId;
|
|
34353
|
+
const showDayHeader = stayLeadsDay || !!day.name?.trim();
|
|
34354
|
+
return (React__default["default"].createElement(React__default["default"].Fragment, { key: `day-${day.order}` },
|
|
34355
|
+
showDayHeader && renderDayHeader(day, stayLeadsDay ? stay : undefined),
|
|
34356
|
+
dayNodes.map((node) => {
|
|
34357
|
+
const item = lineByNodeId.get(node.nodeId);
|
|
34358
|
+
if (node.serviceType === ACCOMMODATION_SERVICE_TYPE) {
|
|
34359
|
+
const stayStart = stayStartByNodeId.get(node.nodeId);
|
|
34360
|
+
const titledByDayHeader = stayLeadsDay && stay.item.node.nodeId === node.nodeId;
|
|
34361
|
+
return renderServiceNode(item, !titledByDayHeader, stayStart && getStayTitle(stayStart));
|
|
34362
|
+
}
|
|
34363
|
+
// Every excursion the day holds is drawn once, in one block, where the first of
|
|
34364
|
+
// them falls in the order — so the activities still sit between the stay and the
|
|
34365
|
+
// flight that follow them.
|
|
34366
|
+
if (node.serviceType === EXCURSION_SERVICE_TYPE) {
|
|
34367
|
+
return node.nodeId === firstExcursionNodeId ? renderDayActivities(day) : null;
|
|
34368
|
+
}
|
|
34369
|
+
return renderFlightNode(item);
|
|
34370
|
+
}),
|
|
34371
|
+
firstExcursionNodeId === undefined && renderDayActivities(day)));
|
|
34372
|
+
}),
|
|
34373
|
+
React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
34374
|
+
React__default["default"].createElement("button", { type: "button", className: "cta", disabled: trajectResolving, onClick: handleShowItinerary }, translations.STEPS.NEXT))));
|
|
33435
34375
|
};
|
|
33436
34376
|
|
|
33437
34377
|
const useTrajectAvailability = () => {
|
|
33438
34378
|
const context = React.useContext(SearchResultsConfigurationContext);
|
|
33439
34379
|
const dispatch = reactRedux.useDispatch();
|
|
33440
34380
|
const trajectEntry = context?.trajectEntry;
|
|
33441
|
-
const
|
|
34381
|
+
const showApiItems = !!context?.showApiItems;
|
|
34382
|
+
const hideShowMoreResults = !!context?.hideShowMoreResults;
|
|
33442
34383
|
React.useEffect(() => {
|
|
33443
34384
|
if (!context || !trajectEntry)
|
|
33444
34385
|
return;
|
|
33445
34386
|
const controller = new AbortController();
|
|
33446
34387
|
dispatch(resetTrajectAvailability());
|
|
34388
|
+
dispatch(setTrajectResolving(true));
|
|
33447
34389
|
const config = {
|
|
33448
34390
|
host: context.tideConnection.host,
|
|
33449
34391
|
apiKey: context.tideConnection.apiKey
|
|
@@ -33470,36 +34412,51 @@ const useTrajectAvailability = () => {
|
|
|
33470
34412
|
const lineGuid = item.line.guid;
|
|
33471
34413
|
const search = item.node.serviceType === EXCURSION_SERVICE_TYPE ? build.searchPackagingExcursions : build.searchPackagingAccommodations;
|
|
33472
34414
|
dispatch(setTrajectNodeLoading(lineGuid));
|
|
34415
|
+
const destination = getNodeSearchDestination(nodesWithLines, item);
|
|
34416
|
+
const searchFor = async (productCode) => {
|
|
34417
|
+
try {
|
|
34418
|
+
const results = (await search(config, buildTrajectNodeRequest(item, searchContext, productCode, destination), controller.signal)) ?? [];
|
|
34419
|
+
return results.filter((x) => showApiItems || !isExternalResult(x));
|
|
34420
|
+
}
|
|
34421
|
+
catch (error) {
|
|
34422
|
+
if (!controller.signal.aborted)
|
|
34423
|
+
console.error('Traject product unavailable', item.node.name, productCode, error);
|
|
34424
|
+
return [];
|
|
34425
|
+
}
|
|
34426
|
+
};
|
|
33473
34427
|
try {
|
|
33474
|
-
|
|
33475
|
-
const
|
|
33476
|
-
|
|
33477
|
-
|
|
33478
|
-
|
|
33479
|
-
|
|
33480
|
-
|
|
33481
|
-
|
|
33482
|
-
|
|
33483
|
-
return
|
|
33484
|
-
|
|
34428
|
+
// With external items switched off the node only looks at products held inside Tide.
|
|
34429
|
+
const candidates = getNodeCandidates(item.node).filter((x) => showApiItems || !isExternalCandidate(x));
|
|
34430
|
+
const areaResults = hideShowMoreResults ? [] : await searchFor('');
|
|
34431
|
+
if (controller.signal.aborted)
|
|
34432
|
+
return [];
|
|
34433
|
+
const configuredResults = [];
|
|
34434
|
+
const found = await Promise.all(candidates.map(async (candidate) => {
|
|
34435
|
+
const known = areaResults.find((x) => matchesCode(x, candidate.code));
|
|
34436
|
+
if (known)
|
|
34437
|
+
return known;
|
|
34438
|
+
return (await searchFor(candidate.code)).find((x) => matchesCode(x, candidate.code)) ?? null;
|
|
33485
34439
|
}));
|
|
33486
|
-
|
|
33487
|
-
|
|
33488
|
-
|
|
34440
|
+
if (controller.signal.aborted)
|
|
34441
|
+
return [];
|
|
34442
|
+
// Two configured codes can land on the same product — a hotel and one of its room types.
|
|
34443
|
+
found.forEach((result) => {
|
|
34444
|
+
if (!result || configuredResults.some((x) => x.code === result.code))
|
|
34445
|
+
return;
|
|
34446
|
+
configuredResults.push(result);
|
|
34447
|
+
});
|
|
34448
|
+
const configuredCodes = new Set(configuredResults.map((x) => x.code));
|
|
34449
|
+
const destinationResults = areaResults.filter((x) => !configuredCodes.has(x.code));
|
|
33489
34450
|
const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
|
|
33490
|
-
|
|
33491
|
-
|
|
33492
|
-
|
|
33493
|
-
: [];
|
|
33494
|
-
const results = [...configuredResults, ...destinationResults];
|
|
34451
|
+
const preferredUnavailable = configuredResults.length
|
|
34452
|
+
? !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code))
|
|
34453
|
+
: candidates.length > 0;
|
|
33495
34454
|
const picked = configuredResults[0] ?? pickCheapest(destinationResults);
|
|
33496
|
-
if (!configuredResults.length)
|
|
33497
|
-
preferredUnavailable = candidates.length > 0;
|
|
33498
34455
|
if (controller.signal.aborted)
|
|
33499
34456
|
return [];
|
|
33500
34457
|
dispatch(setTrajectNodeAvailability({
|
|
33501
34458
|
lineGuid,
|
|
33502
|
-
results,
|
|
34459
|
+
results: [...configuredResults, ...destinationResults],
|
|
33503
34460
|
selectedCode: picked?.code ?? null,
|
|
33504
34461
|
preferredUnavailable
|
|
33505
34462
|
}));
|
|
@@ -33543,11 +34500,11 @@ const useTrajectAvailability = () => {
|
|
|
33543
34500
|
}
|
|
33544
34501
|
};
|
|
33545
34502
|
(async () => {
|
|
33546
|
-
const
|
|
33547
|
-
const
|
|
33548
|
-
const unresolved = (await Promise.all([...searchable.map(resolveNode), ...flightNodes.map(resolveFlightNode)])).flat();
|
|
34503
|
+
const resolvable = nodesWithLines.filter((x) => isSearchableTrajectNode(x.node) || isTrajectFlightNode(x.node));
|
|
34504
|
+
const unresolved = (await Promise.all(resolvable.map((item) => (isTrajectFlightNode(item.node) ? resolveFlightNode(item) : resolveNode(item))))).flat();
|
|
33549
34505
|
if (controller.signal.aborted)
|
|
33550
34506
|
return;
|
|
34507
|
+
dispatch(setTrajectResolving(false));
|
|
33551
34508
|
// A node with no availability anywhere cannot be priced or booked, so its line comes off
|
|
33552
34509
|
// the entry. The card stays visible and reports the gap.
|
|
33553
34510
|
const emptyLineGuids = unresolved.filter(Boolean);
|
|
@@ -33555,7 +34512,7 @@ const useTrajectAvailability = () => {
|
|
|
33555
34512
|
dispatch(removeEditableEntryLines(emptyLineGuids));
|
|
33556
34513
|
})();
|
|
33557
34514
|
return () => controller.abort();
|
|
33558
|
-
}, [trajectEntry,
|
|
34515
|
+
}, [trajectEntry, showApiItems, hideShowMoreResults]);
|
|
33559
34516
|
};
|
|
33560
34517
|
|
|
33561
34518
|
const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
@@ -33563,10 +34520,12 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
33563
34520
|
const dispatch = reactRedux.useDispatch();
|
|
33564
34521
|
const context = React.useContext(SearchResultsConfigurationContext);
|
|
33565
34522
|
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
33566
|
-
const { results, filteredResults, packagingAccoResults, filteredPackagingAccoResults, isLoading, flightsLoading, initialFilters, filters, flightFilters, selectedSortType, selectedFlightSortType, selectedSearchResult, selectedPackagingAccoResultCode, flyInIsOpen, packagingAccoSearchDetails, editablePackagingEntry, transactionId, flyInType, packagingFlightResults, confirmedExcursionsByDay, bookPackagingEntry, trajectEditLineGuid } = reactRedux.useSelector((state) => state.searchResults);
|
|
34523
|
+
const { results, filteredResults, packagingAccoResults, filteredPackagingAccoResults, isLoading, flightsLoading, initialFilters, filters, flightFilters, selectedSortType, selectedFlightSortType, selectedSearchResult, selectedPackagingAccoResultCode, flyInIsOpen, packagingAccoSearchDetails, editablePackagingEntry, transactionId, flyInType, packagingFlightResults, confirmedExcursionsByDay, bookPackagingEntry, trajectEditLineGuid, trajectResolving, trajectItineraryView } = reactRedux.useSelector((state) => state.searchResults);
|
|
33567
34524
|
const isMobile = useMediaQuery('(max-width: 1200px)');
|
|
33568
34525
|
// Resolves live availability per traject node; no-op unless a traject entry is configured.
|
|
33569
34526
|
useTrajectAvailability();
|
|
34527
|
+
// The entry as the backend can price it: a traject swaps in the guids its searches cached.
|
|
34528
|
+
const bookableEntry = useBookableEntry();
|
|
33570
34529
|
const [initialFiltersSet, setInitialFiltersSet] = React.useState(false);
|
|
33571
34530
|
const [initialFlightFiltersSet, setInitialFlightFiltersSet] = React.useState(false);
|
|
33572
34531
|
const [filtersOpen, setFiltersOpen] = React.useState(false);
|
|
@@ -34333,6 +35292,10 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34333
35292
|
const fetchPriceDetails = async () => {
|
|
34334
35293
|
if (!context || !editablePackagingEntry || lodash.isEmpty(editablePackagingEntry.lines))
|
|
34335
35294
|
return;
|
|
35295
|
+
// A traject resolves one node at a time. Pricing each step would show a total that climbs
|
|
35296
|
+
// as the trip fills in, so the whole trip is priced once, when the last node is in.
|
|
35297
|
+
if (context.trajectEntry && trajectResolving)
|
|
35298
|
+
return;
|
|
34336
35299
|
setPricesAreLoading(true);
|
|
34337
35300
|
try {
|
|
34338
35301
|
const config = {
|
|
@@ -34344,7 +35307,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34344
35307
|
officeId: context.tideConnection.officeId,
|
|
34345
35308
|
catalogueId: context.searchConfiguration.defaultCatalogueId ?? lodash.first(context?.tideConnection?.catalogueIds) ?? 1,
|
|
34346
35309
|
agentId: context.agentId,
|
|
34347
|
-
payload: editablePackagingEntry
|
|
35310
|
+
payload: bookableEntry ?? editablePackagingEntry
|
|
34348
35311
|
};
|
|
34349
35312
|
const priceDetails = await build.getPriceDetails(config, request);
|
|
34350
35313
|
dispatch(setPriceDetails(priceDetails));
|
|
@@ -34358,8 +35321,9 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34358
35321
|
const fetchItinerary = async () => {
|
|
34359
35322
|
if (!context || !context.packagingEntry || !editablePackagingEntry || lodash.isEmpty(editablePackagingEntry.lines))
|
|
34360
35323
|
return;
|
|
34361
|
-
//
|
|
34362
|
-
|
|
35324
|
+
// While a traject is being composed its own day-by-day column is the itinerary; the generated
|
|
35325
|
+
// one is what the traveller reads the finished trip back from, so it waits until then.
|
|
35326
|
+
if (context.trajectEntry && (trajectResolving || !trajectItineraryView))
|
|
34363
35327
|
return;
|
|
34364
35328
|
setItineraryIsLoading(true);
|
|
34365
35329
|
try {
|
|
@@ -34372,7 +35336,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34372
35336
|
officeId: context.tideConnection.officeId,
|
|
34373
35337
|
catalogueId: context.searchConfiguration.defaultCatalogueId ?? lodash.first(context?.tideConnection?.catalogueIds) ?? 1,
|
|
34374
35338
|
agentId: context.agentId,
|
|
34375
|
-
payload: editablePackagingEntry
|
|
35339
|
+
payload: bookableEntry ?? editablePackagingEntry
|
|
34376
35340
|
};
|
|
34377
35341
|
const itinerary = await build.getItinerary(config, request);
|
|
34378
35342
|
dispatch(setItinerary(itinerary));
|
|
@@ -34385,7 +35349,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34385
35349
|
};
|
|
34386
35350
|
fetchPriceDetails();
|
|
34387
35351
|
fetchItinerary();
|
|
34388
|
-
}, [editablePackagingEntry]);
|
|
35352
|
+
}, [editablePackagingEntry, bookableEntry, trajectResolving, trajectItineraryView]);
|
|
34389
35353
|
// Flight selection
|
|
34390
35354
|
// const [selectedOutwardKey, setSelectedOutwardKey] = useState<string | null>(null);
|
|
34391
35355
|
// const [selectedReturnKey, setSelectedReturnKey] = useState<string | null>(null);
|
|
@@ -34734,6 +35698,11 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34734
35698
|
address: {}
|
|
34735
35699
|
};
|
|
34736
35700
|
};
|
|
35701
|
+
const handleComposeTraject = () => {
|
|
35702
|
+
dispatch(setTrajectItineraryView(false));
|
|
35703
|
+
if (typeof window !== 'undefined')
|
|
35704
|
+
window.scrollTo(0, 0);
|
|
35705
|
+
};
|
|
34737
35706
|
const handleShowMoreFlights = (flyInType) => {
|
|
34738
35707
|
dispatch(setFlyInType(flyInType));
|
|
34739
35708
|
dispatch(setFlyInIsOpen(true));
|
|
@@ -34749,7 +35718,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34749
35718
|
context.searchConfiguration.qsmType != build.PortalQsmType.AccommodationAndFlight && context.showFilters && (React__default["default"].createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => setFiltersOpen(!filtersOpen),
|
|
34750
35719
|
// handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
|
|
34751
35720
|
isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) })),
|
|
34752
|
-
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default["default"].createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading, onEditAccommodation: handleEditAccommodation })),
|
|
35721
|
+
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default["default"].createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading || trajectResolving, onEditAccommodation: handleEditAccommodation })),
|
|
34753
35722
|
React__default["default"].createElement("div", { className: "search__results" },
|
|
34754
35723
|
isMobile && (React__default["default"].createElement("div", { className: "search__result-row" },
|
|
34755
35724
|
React__default["default"].createElement("div", { className: "search__results__actions" },
|
|
@@ -34823,7 +35792,11 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34823
35792
|
React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" },
|
|
34824
35793
|
selectedReturnKey && selectedReturn && (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${selectedReturnKey}`, item: selectedReturn.return, guid: selectedReturn.outwardGuid, selectedGuid: selectedReturn.outwardGuid, isOutward: false, showSelectedState: true, price: selectedReturn.price })),
|
|
34825
35794
|
uniqueReturnFlights.map((result) => (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${result.outwardGuid}`, item: result.return, onSelect: () => dispatch(setSelectedReturnKey(getFlightKey(result.return.segments))), guid: result.outwardGuid, isOutward: false, currentSelectedPrice: selectedReturn?.price, price: result.price })))))),
|
|
34826
|
-
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.trajectEntry && (React__default["default"].createElement(TrajectResults, { isLoading: isLoading })),
|
|
35795
|
+
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.trajectEntry && !trajectItineraryView && (React__default["default"].createElement(TrajectResults, { isLoading: isLoading })),
|
|
35796
|
+
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.trajectEntry && trajectItineraryView && (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
35797
|
+
React__default["default"].createElement("div", { className: "search__results__cards__actions" },
|
|
35798
|
+
React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: handleComposeTraject }, translations.STEPS.PREVIOUS)),
|
|
35799
|
+
React__default["default"].createElement(FullItinerary, { isLoading: itineraryIsLoading }))),
|
|
34827
35800
|
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.packagingEntry && !context.trajectEntry && (React__default["default"].createElement(FullItinerary, { isLoading: itineraryIsLoading })))),
|
|
34828
35801
|
React__default["default"].createElement(FlyIn, { srpType: context.searchConfiguration.qsmType, isOpen: flyInIsOpen, setIsOpen: handleFlyInToggle, handleConfirm: () => handleConfirmHotelSwap(), onPanelRef: (el) => (panelRef.current = el), detailsLoading: detailsIsLoading, flyInType: flyInType, isPackageEditFlow: !!context.packagingEntry, sortByTypes: sortByTypes, activeSearchSeed: activeSearchSeed, toggleFilters: () => setFiltersOpen(!filtersOpen), filtersOpen: filtersOpen })))))))));
|
|
34829
35802
|
};
|
|
@@ -37732,20 +38705,20 @@ function ItineraryMapView({ destinations: propDestinations, adults = 2, dateFrom
|
|
|
37732
38705
|
const isStartEnd = dest.isStart || dest.isEnd;
|
|
37733
38706
|
let iconHtml;
|
|
37734
38707
|
if (isStartEnd) {
|
|
37735
|
-
iconHtml = `
|
|
37736
|
-
<div class="map-view__marker map-view__marker--plane">
|
|
37737
|
-
<svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
|
37738
|
-
<path d="M482.3 192c34.2 0 93.7 29 93.7 64c0 36-59.5 64-93.7 64l-116.6 0L265.2 495.9c-5.7 10.1-16.3 16.1-27.8 16.1l-56.2 0c-10.6 0-18.3-10.3-15.8-20.6l49-196.3L112 288 68.8 377.6c-3 6.1-9.2 10.4-16 10.4l-2.8 0c-9.2 0-16-8.8-13.3-17.6L80 256L36.7 141.6C34 132.8 40.8 124 50 124l2.8 0c6.8 0 13 4.3 16 10.4L112 224l102.9 0-49-196.3C163.5 17.3 171.2 7 181.8 7l56.2 0c11.5 0 22.1 6 27.8 16.1L365.7 192l116.6 0z"/>
|
|
37739
|
-
</svg>
|
|
38708
|
+
iconHtml = `
|
|
38709
|
+
<div class="map-view__marker map-view__marker--plane">
|
|
38710
|
+
<svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
|
38711
|
+
<path d="M482.3 192c34.2 0 93.7 29 93.7 64c0 36-59.5 64-93.7 64l-116.6 0L265.2 495.9c-5.7 10.1-16.3 16.1-27.8 16.1l-56.2 0c-10.6 0-18.3-10.3-15.8-20.6l49-196.3L112 288 68.8 377.6c-3 6.1-9.2 10.4-16 10.4l-2.8 0c-9.2 0-16-8.8-13.3-17.6L80 256L36.7 141.6C34 132.8 40.8 124 50 124l2.8 0c6.8 0 13 4.3 16 10.4L112 224l102.9 0-49-196.3C163.5 17.3 171.2 7 181.8 7l56.2 0c11.5 0 22.1 6 27.8 16.1L365.7 192l116.6 0z"/>
|
|
38712
|
+
</svg>
|
|
37740
38713
|
</div>`;
|
|
37741
38714
|
}
|
|
37742
38715
|
else {
|
|
37743
38716
|
stopIndex++;
|
|
37744
38717
|
const imgStyle = dest.imageUrl ? `style="background-image:url('${dest.imageUrl}')"` : '';
|
|
37745
|
-
iconHtml = `
|
|
37746
|
-
<div class="map-view__marker map-view__marker--stop" ${imgStyle}>
|
|
37747
|
-
${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
|
|
37748
|
-
<span class="map-view__marker-badge">${stopIndex}</span>
|
|
38718
|
+
iconHtml = `
|
|
38719
|
+
<div class="map-view__marker map-view__marker--stop" ${imgStyle}>
|
|
38720
|
+
${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
|
|
38721
|
+
<span class="map-view__marker-badge">${stopIndex}</span>
|
|
37749
38722
|
</div>`;
|
|
37750
38723
|
}
|
|
37751
38724
|
const icon = L.divIcon({
|