@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.
Files changed (25) hide show
  1. package/build/build-cjs/index.js +1368 -395
  2. package/build/build-cjs/src/search-results/components/itinerary/itinerary-utils.d.ts +12 -0
  3. package/build/build-cjs/src/search-results/components/itinerary/traject-itinerary.d.ts +9 -0
  4. package/build/build-cjs/src/search-results/components/traject/traject-day-activities-flyin.d.ts +6 -0
  5. package/build/build-cjs/src/search-results/components/traject/traject-flights-flyin.d.ts +13 -0
  6. package/build/build-cjs/src/search-results/components/traject/traject-node-card.d.ts +6 -3
  7. package/build/build-cjs/src/search-results/hooks/use-bookable-entry.d.ts +11 -0
  8. package/build/build-cjs/src/search-results/hooks/use-traject-day-activities.d.ts +6 -0
  9. package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +14 -2
  10. package/build/build-cjs/src/search-results/types.d.ts +12 -2
  11. package/build/build-cjs/src/search-results/utils/traject-utils.d.ts +76 -2
  12. package/build/build-esm/index.js +1368 -395
  13. package/build/build-esm/src/search-results/components/itinerary/itinerary-utils.d.ts +12 -0
  14. package/build/build-esm/src/search-results/components/itinerary/traject-itinerary.d.ts +9 -0
  15. package/build/build-esm/src/search-results/components/traject/traject-day-activities-flyin.d.ts +6 -0
  16. package/build/build-esm/src/search-results/components/traject/traject-flights-flyin.d.ts +13 -0
  17. package/build/build-esm/src/search-results/components/traject/traject-node-card.d.ts +6 -3
  18. package/build/build-esm/src/search-results/hooks/use-bookable-entry.d.ts +11 -0
  19. package/build/build-esm/src/search-results/hooks/use-traject-day-activities.d.ts +6 -0
  20. package/build/build-esm/src/search-results/store/search-results-slice.d.ts +14 -2
  21. package/build/build-esm/src/search-results/types.d.ts +12 -2
  22. package/build/build-esm/src/search-results/utils/traject-utils.d.ts +76 -2
  23. package/package.json +2 -2
  24. package/styles/booking-search-results-variables.scss +6 -0
  25. package/styles/components/_search.scss +94 -5
@@ -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 640 640", fill: fill ?? 'currentColor' },
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
- trajectEditLineGuid: null
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
- outboundFlight && (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport" },
27957
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline" },
27958
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
27959
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-item" },
27960
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" },
27961
- React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-date-date" },
27962
- React__default["default"].createElement("strong", null, dateFns.format(new Date(outboundFlight.from), 'd'))),
27963
- React__default["default"].createElement("p", null, dateFns.format(new Date(outboundFlight.from), 'MMM'))),
27964
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-badge" },
27965
- React__default["default"].createElement(Icon, { name: "ui-plane", height: 15 })),
27966
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-details" },
27967
- React__default["default"].createElement("h6", null, outboundFlight.productName),
27968
- React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-plane" },
27969
- React__default["default"].createElement("span", null,
27970
- React__default["default"].createElement(Icon, { name: "ui-plane-depart", height: 14 }),
27971
- " ",
27972
- React__default["default"].createElement("strong", null, getDepartureTime(outboundFlight))),
27973
- ' ',
27974
- "-",
27975
- ' ',
27976
- React__default["default"].createElement("span", null,
27977
- React__default["default"].createElement(Icon, { name: "ui-plane-arrive", height: 14 }),
27978
- " ",
27979
- React__default["default"].createElement("strong", null, getArrivalTime(outboundFlight)))),
27980
- React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-time" },
27981
- React__default["default"].createElement("span", null,
27982
- React__default["default"].createElement(Icon, { name: "ui-clock", height: 20 }),
27983
- " ",
27984
- getDuration(outboundFlight))))))),
27985
- !lodash.isEmpty(groupedOtherSegments) &&
27986
- groupedOtherSegments.map((group) => {
27987
- const firstSegment = lodash.first(group.segments);
27988
- if (!firstSegment)
27989
- return null;
27990
- return (React__default["default"].createElement("div", { className: "search__filter__itinerary__segments" },
27991
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segments__wrapper", key: group.key },
27992
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segments-timeline" },
27993
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segments-timeline-line" })),
27994
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segment" },
27995
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-item search__filter__itinerary__segment-item--start" },
27996
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" },
27997
- React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-date-date" },
27998
- React__default["default"].createElement("strong", null, dateFns.format(new Date(firstSegment.from), 'd'))),
27999
- React__default["default"].createElement("p", null, dateFns.format(new Date(firstSegment.from), 'MMM'))),
28000
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-badge" },
28001
- React__default["default"].createElement(Icon, { name: "ui-location", width: 15, height: 15 })),
28002
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details" },
28003
- React__default["default"].createElement("h6", null,
28004
- firstSegment.location?.name || firstSegment.oord?.name || firstSegment.region?.name,
28005
- (firstSegment.location?.name || firstSegment.oord?.name || firstSegment.region?.name) && firstSegment.country?.name ? ', ' : '',
28006
- firstSegment.country?.name),
28007
- React__default["default"].createElement("p", { className: "search__filter__itinerary__segment-details-text" },
28008
- dateFns.format(new Date(firstSegment.from), 'EEE. d MMM yyyy'),
28009
- "> ",
28010
- dateFns.format(new Date(firstSegment.to), 'EEE. d MMM yyyy'))))),
28011
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segment" },
28012
- React__default["default"].createElement("div", { className: `search__filter__itinerary__segment-item ${canEdit(firstSegment) ? 'search__filter__itinerary__segment-item--editable' : ''}`, onClick: () => {
28013
- if (canEdit(firstSegment) && onEditAccommodation) {
28014
- onEditAccommodation(group.segments);
28015
- }
28016
- } },
28017
- 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,
28018
- React__default["default"].createElement("p", { className: "search__filter__itinerary__segment-date-date" },
28019
- React__default["default"].createElement("strong", null, numberOfNights(firstSegment))),
28020
- React__default["default"].createElement(Icon, { name: "ui-moon", width: 16, height: 16 })))),
28021
- getSegmentIcon(firstSegment),
28022
- React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details" },
28023
- React__default["default"].createElement("h6", null, getSegmentTitle(firstSegment)),
28024
- firstSegment.serviceType === ACCOMMODATION_SERVICE_TYPE &&
28025
- group.segments.map((segment, index) => (React__default["default"].createElement(React__default["default"].Fragment, { key: segment.guid },
28026
- React__default["default"].createElement("strong", null,
28027
- translations.SHARED.ROOM,
28028
- "\u00A0",
28029
- index + 1),
28030
- segment.productName && segment.accommodationName && (React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details__room" },
28031
- React__default["default"].createElement(Icon, { name: "ui-bed", width: 12, height: 12 }),
28032
- segment.accommodationName)),
28033
- segment.regimeName && (React__default["default"].createElement("div", { className: "search__filter__itinerary__segment-details__room" },
28034
- React__default["default"].createElement(Icon, { name: "ui-utensils", width: 12, height: 12 }),
28035
- segment.regimeName)))))))))));
28036
- }),
28037
- returnFlight && returnFlight !== outboundFlight && (React__default["default"].createElement("div", { className: "search__filter__itinerary__transport" },
28038
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline" },
28039
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
28040
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-item" },
28041
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-date" },
28042
- React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-date-date" },
28043
- React__default["default"].createElement("strong", null, dateFns.format(new Date(returnFlight.from), 'd'))),
28044
- React__default["default"].createElement("p", null, dateFns.format(new Date(returnFlight.from), 'MMM'))),
28045
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-badge" },
28046
- React__default["default"].createElement(Icon, { name: "ui-plane", height: 15 })),
28047
- React__default["default"].createElement("div", { className: "search__filter__itinerary__transport-details" },
28048
- React__default["default"].createElement("h6", null, returnFlight.productName),
28049
- React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-plane" },
28050
- React__default["default"].createElement("span", null,
28051
- React__default["default"].createElement(Icon, { name: "ui-plane-depart", height: 14 }),
28052
- " ",
28053
- React__default["default"].createElement("strong", null, getDepartureTime(returnFlight))),
28054
- ' ',
28055
- "-",
28056
- ' ',
28057
- React__default["default"].createElement("span", null,
28058
- React__default["default"].createElement(Icon, { name: "ui-plane-arrive", height: 14 }),
28059
- " ",
28060
- React__default["default"].createElement("strong", null, getArrivalTime(returnFlight)))),
28061
- React__default["default"].createElement("p", { className: "search__filter__itinerary__transport-details-time" },
28062
- React__default["default"].createElement("span", null,
28063
- React__default["default"].createElement(Icon, { name: "ui-clock", height: 20 }),
28064
- " ",
28065
- getDuration(returnFlight))))))),
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, travellerCount) => {
31226
+ const optionAppliesToAllTravellers = (option, travellerIds) => {
30437
31227
  const paxIds = getOptionPaxIds(option);
30438
- const expected = Array.from({ length: travellerCount }, (_, i) => i);
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 travellerCount = editablePackagingEntry.pax.length;
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, travellerCount));
30470
- }, [allOptions, travellerCount]);
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, travellerCount));
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, travellerCount]);
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, travellerCount) && option.isSelected);
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, travellerCount) &&
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, travellerCount);
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, travellerCount) && option.accommodationCode === accommodationCode);
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 = ({ item, result, isSelected, languageCode, translations, onSelect, onEditOptions, showNights }) => {
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 search__results__cards--compact" }, ordered.map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, item: item, result: result, isSelected: result.code === availability?.selectedCode, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(result.code), showNights: item.node.serviceType === ACCOMMODATION_SERVICE_TYPE })))))));
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
- let updatedEditablePackagingEntry = {
33237
- ...editablePackagingEntry,
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
- updatedEditablePackagingEntry = {
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
- payload: updatedEditablePackagingEntry
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 renderServiceNode = (item) => {
33347
- console.log('renderServiceNode', item);
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
- status === 'loading' && (React__default["default"].createElement(Spinner, { label: node.serviceType === EXCURSION_SERVICE_TYPE ? translations.SRP.LOADING_EXCURSIONS : translations.SRP.LOADING_ACCOMMODATIONS })),
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--compact" }, [selected, ...configured].map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, item: item, 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 })))),
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 isExpanded = !!expandedNodes[line.guid];
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: () => toggleExpanded(line.guid) }, isExpanded ? translations.SRP.SHOW_LESS : `${translations.SRP.SHOW_MORE} (${others.length - 2})`)))))));
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, trajectEntry.traject.days.map((day) => {
33418
- const dayNodes = nodesByDay.get(day.order) ?? [];
33419
- if (!dayNodes.length)
33420
- return null;
33421
- return (React__default["default"].createElement(React__default["default"].Fragment, { key: `day-${day.order}` }, dayNodes.map((node) => {
33422
- if (node.serviceType !== ACCOMMODATION_SERVICE_TYPE && node.serviceType !== EXCURSION_SERVICE_TYPE && node.serviceType !== FLIGHT_SERVICE_TYPE)
33423
- return null;
33424
- // if (node.trajectNodeType === CONNECTING_TRAJECT_NODE_TYPE) return null;
33425
- // if (node.trajectNodeType !== REGULAR_TRAJECT_NODE_TYPE) return renderContextNode(node);
33426
- if (node.trajectNodeType !== REGULAR_TRAJECT_NODE_TYPE)
33427
- return null;
33428
- const item = lineByNodeId.get(node.nodeId);
33429
- // if (!item) return renderContextNode(node);
33430
- if (!item)
33431
- return null;
33432
- return isTrajectFlightNode(node) ? renderFlightNode(item) : renderServiceNode(item);
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 showNonPreferredItems = !!context?.showNonPreferredItems;
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
- const candidates = getNodeCandidates(item.node);
33475
- const attempts = await Promise.all(candidates.map(async (candidate) => {
33476
- try {
33477
- const attempt = await search(config, buildTrajectNodeRequest(item, searchContext, candidate.code), controller.signal);
33478
- return (attempt ?? []).find((result) => matchesCode(result, candidate.code)) ?? null;
33479
- }
33480
- catch (error) {
33481
- if (!controller.signal.aborted)
33482
- console.error('Traject hotel unavailable', candidate.code, error);
33483
- return null;
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
- const configuredResults = attempts
33487
- .filter((x) => !!x)
33488
- .filter((x, i, all) => all.findIndex((y) => y.code === x.code) === i);
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
- let preferredUnavailable = !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code));
33491
- const destinationResults = showNonPreferredItems
33492
- ? ((await search(config, buildTrajectNodeRequest(item, searchContext, ''), controller.signal)) ?? []).filter((x) => !configuredResults.some((configured) => configured.code === x.code))
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 searchable = nodesWithLines.filter((x) => isSearchableTrajectNode(x.node));
33547
- const flightNodes = nodesWithLines.filter((x) => isTrajectFlightNode(x.node));
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, showNonPreferredItems]);
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
- // The traject view renders its own day-by-day column, so the generated itinerary is unused.
34362
- if (context.trajectEntry)
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({