@qite/tide-components 1.0.7 → 1.0.9

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.
@@ -2,7 +2,7 @@
2
2
  import * as React from 'react';
3
3
  import React__default, { useLayoutEffect as useLayoutEffect$1, useEffect, useContext, useState, useRef, useCallback, useMemo } from 'react';
4
4
  import JsonURL from '@jsonurl/jsonurl';
5
- import { format as format$2, isToday, getDate, startOfWeek, startOfMonth, endOfWeek, addWeeks, endOfMonth, eachDayOfInterval, getYear, getMonth, getISOWeek, getISODay, isSameMonth, startOfDay, isAfter, isEqual, isWithinInterval, endOfDay, isBefore, isSameDay, differenceInCalendarDays, addMonths, addDays, formatISO, addYears, differenceInYears, parseISO, differenceInMinutes, isValid, parse, startOfToday } from 'date-fns';
5
+ import { format as format$2, isToday, getDate, startOfWeek, startOfMonth, endOfWeek, addWeeks, endOfMonth, eachDayOfInterval, getYear, getMonth, getISOWeek, getISODay, isSameMonth, startOfDay, isAfter, isSameDay, isEqual, isWithinInterval, endOfDay, isBefore, differenceInCalendarDays, addMonths, addDays, formatISO, addYears, differenceInYears, parseISO, differenceInMinutes, isValid, parse, startOfToday } from 'date-fns';
6
6
  import { merge, cloneDeep, isNil, isArray as isArray$1, compact, isEmpty as isEmpty$1, range, chunk, isFunction as isFunction$1, clamp, now, omit, isPlainObject as isPlainObject$1, mapValues, pickBy, first, orderBy, uniq, uniqBy, sortBy, sum, last, findIndex as findIndex$1, set, get, isEqual as isEqual$1, groupBy, minBy, concat, flatMap } from 'lodash';
7
7
  import { arSA, da, de, enGB, es, fr, is, it, nl, nb, pl, pt, sv, ja, enUS } from 'date-fns/locale';
8
8
  import { usePopper } from 'react-popper';
@@ -388,6 +388,8 @@ var SRP$f = {
388
388
  DEPARTURE_ASC: "تاريخ المغادرة (الأقرب أولاً)",
389
389
  FILTERS: "عوامل التصفية",
390
390
  SHOW_ITINERARY: "عرض خط سير الرحلة",
391
+ VIEW_FULL_ITINERARY: "عرض خط السير الكامل",
392
+ SHOW_SERVICES: "عرض الخدمات",
391
393
  VIEW_BOOKING: "عرض الحجز",
392
394
  ITINERARY_TITLE: "ملخص رحلتك",
393
395
  DOSSIER_NUMBER: "رقم الملف",
@@ -429,6 +431,7 @@ var SRP$f = {
429
431
  TRAVEL_GROUP: "مجموعة المسافرين",
430
432
  EXCURSION: "رحلة",
431
433
  LOADING_EXCURSIONS: "جارٍ تحميل الرحلات...",
434
+ LOADING_ACTIVITIES: "جارٍ تحميل الأنشطة...",
432
435
  LOADING_FLIGHTS: "جارٍ تحميل الرحلات الجوية...",
433
436
  LOADING_ACCOMMODATIONS: "جارٍ تحميل الإقامات...",
434
437
  LOADING_ITINERARY: "جارٍ تحميل خط سير الرحلة...",
@@ -832,6 +835,8 @@ var SRP$e = {
832
835
  DEPARTURE_ASC: "Afrejsedato (snarest først)",
833
836
  FILTERS: "Filtre",
834
837
  SHOW_ITINERARY: "Vis rejseplan",
838
+ VIEW_FULL_ITINERARY: "Se hele rejseplanen",
839
+ SHOW_SERVICES: "Vis ydelser",
835
840
  VIEW_BOOKING: "Se booking",
836
841
  ITINERARY_TITLE: "Din rejseoversigt",
837
842
  DOSSIER_NUMBER: "Sagsnummer",
@@ -873,6 +878,7 @@ var SRP$e = {
873
878
  TRAVEL_GROUP: "Rejseselskab",
874
879
  EXCURSION: "Udflugt",
875
880
  LOADING_EXCURSIONS: "Indlæser udflugter...",
881
+ LOADING_ACTIVITIES: "Indlæser aktiviteter...",
876
882
  LOADING_FLIGHTS: "Indlæser fly...",
877
883
  LOADING_ACCOMMODATIONS: "Indlæser indkvarteringer...",
878
884
  LOADING_ITINERARY: "Indlæser rejseplan...",
@@ -1280,6 +1286,8 @@ var SRP$d = {
1280
1286
  DEPARTURE_ASC: "Abreisedatum (früheste zuerst)",
1281
1287
  FILTERS: "Filter",
1282
1288
  SHOW_ITINERARY: "Reiseverlauf anzeigen",
1289
+ VIEW_FULL_ITINERARY: "Vollständigen Reiseverlauf ansehen",
1290
+ SHOW_SERVICES: "Leistungen anzeigen",
1283
1291
  VIEW_BOOKING: "Buchung anzeigen",
1284
1292
  ITINERARY_TITLE: "Ihre Reiseübersicht",
1285
1293
  DOSSIER_NUMBER: "Vorgangsnummer",
@@ -1317,6 +1325,7 @@ var SRP$d = {
1317
1325
  TRAVEL_GROUP: "Reisegruppe",
1318
1326
  EXCURSION: "Ausflug",
1319
1327
  LOADING_EXCURSIONS: "Lade Ausflüge...",
1328
+ LOADING_ACTIVITIES: "Lade Aktivitäten...",
1320
1329
  LOADING_FLIGHTS: "Lade Flüge...",
1321
1330
  LOADING_ACCOMMODATIONS: "Lade Unterkünfte...",
1322
1331
  LOADING_ITINERARY: "Lade Reiseverlauf...",
@@ -1728,6 +1737,8 @@ var SRP$c = {
1728
1737
  DEPARTURE_ASC: "Departure date (soonest first)",
1729
1738
  FILTERS: "Filters",
1730
1739
  SHOW_ITINERARY: "Show itinerary",
1740
+ VIEW_FULL_ITINERARY: "View full itinerary",
1741
+ SHOW_SERVICES: "Show services",
1731
1742
  VIEW_BOOKING: "View booking",
1732
1743
  ITINERARY_TITLE: "Your trip summary",
1733
1744
  DOSSIER_NUMBER: "File number",
@@ -1765,6 +1776,7 @@ var SRP$c = {
1765
1776
  TRAVEL_GROUP: "Travel group",
1766
1777
  EXCURSION: "Excursion",
1767
1778
  LOADING_EXCURSIONS: "Loading excursions...",
1779
+ LOADING_ACTIVITIES: "Loading activities...",
1768
1780
  LOADING_FLIGHTS: "Loading flights...",
1769
1781
  LOADING_ACCOMMODATIONS: "Loading accommodations...",
1770
1782
  LOADING_ITINERARY: "Loading itinerary...",
@@ -2172,6 +2184,8 @@ var SRP$b = {
2172
2184
  DEPARTURE_ASC: "Fecha de salida (más próxima primero)",
2173
2185
  FILTERS: "Filtros",
2174
2186
  SHOW_ITINERARY: "Mostrar itinerario",
2187
+ VIEW_FULL_ITINERARY: "Ver itinerario completo",
2188
+ SHOW_SERVICES: "Ver servicios",
2175
2189
  VIEW_BOOKING: "Ver reserva",
2176
2190
  ITINERARY_TITLE: "Resumen de tu viaje",
2177
2191
  DOSSIER_NUMBER: "Número de expediente",
@@ -2209,6 +2223,7 @@ var SRP$b = {
2209
2223
  TRAVEL_GROUP: "Grupo de viaje",
2210
2224
  EXCURSION: "Excursión",
2211
2225
  LOADING_EXCURSIONS: "Cargando excursiones...",
2226
+ LOADING_ACTIVITIES: "Cargando actividades...",
2212
2227
  LOADING_FLIGHTS: "Cargando vuelos...",
2213
2228
  LOADING_ACCOMMODATIONS: "Cargando alojamientos...",
2214
2229
  LOADING_ITINERARY: "Cargando itinerario...",
@@ -2620,6 +2635,8 @@ var SRP$a = {
2620
2635
  DEPARTURE_ASC: "Date de départ (la plus proche d’abord)",
2621
2636
  FILTERS: "Filtres",
2622
2637
  SHOW_ITINERARY: "Afficher l’itinéraire",
2638
+ VIEW_FULL_ITINERARY: "Voir l'itinéraire complet",
2639
+ SHOW_SERVICES: "Voir les services",
2623
2640
  VIEW_BOOKING: "Voir la réservation",
2624
2641
  ITINERARY_TITLE: "Résumé de votre voyage",
2625
2642
  DOSSIER_NUMBER: "Numéro de dossier",
@@ -2657,6 +2674,7 @@ var SRP$a = {
2657
2674
  TRAVEL_GROUP: "Groupe de voyageurs",
2658
2675
  EXCURSION: "Excursion",
2659
2676
  LOADING_EXCURSIONS: "Chargement des excursions...",
2677
+ LOADING_ACTIVITIES: "Chargement des activités...",
2660
2678
  LOADING_FLIGHTS: "Chargement des vols...",
2661
2679
  LOADING_ACCOMMODATIONS: "Chargement des hébergements...",
2662
2680
  LOADING_ITINERARY: "Chargement de l’itinéraire...",
@@ -3064,6 +3082,8 @@ var SRP$9 = {
3064
3082
  DEPARTURE_ASC: "Date de départ (la plus proche d’abord)",
3065
3083
  FILTERS: "Filtres",
3066
3084
  SHOW_ITINERARY: "Afficher l’itinéraire",
3085
+ VIEW_FULL_ITINERARY: "Voir l'itinéraire complet",
3086
+ SHOW_SERVICES: "Voir les services",
3067
3087
  VIEW_BOOKING: "Voir la réservation",
3068
3088
  ITINERARY_TITLE: "Résumé de votre voyage",
3069
3089
  DOSSIER_NUMBER: "Numéro de dossier",
@@ -3101,6 +3121,7 @@ var SRP$9 = {
3101
3121
  TRAVEL_GROUP: "Groupe de voyageurs",
3102
3122
  EXCURSION: "Excursion",
3103
3123
  LOADING_EXCURSIONS: "Chargement des excursions...",
3124
+ LOADING_ACTIVITIES: "Chargement des activités...",
3104
3125
  LOADING_FLIGHTS: "Chargement des vols...",
3105
3126
  LOADING_ACCOMMODATIONS: "Chargement des hébergements...",
3106
3127
  LOADING_ITINERARY: "Chargement de l’itinéraire...",
@@ -3508,6 +3529,8 @@ var SRP$8 = {
3508
3529
  DEPARTURE_ASC: "Brottfarardagur (nærst fyrst)",
3509
3530
  FILTERS: "Síur",
3510
3531
  SHOW_ITINERARY: "Sýna ferðaráætlun",
3532
+ VIEW_FULL_ITINERARY: "Skoða alla ferðaáætlunina",
3533
+ SHOW_SERVICES: "Sýna þjónustu",
3511
3534
  VIEW_BOOKING: "Skoða bókun",
3512
3535
  ITINERARY_TITLE: "Yfirlit ferðarinnar",
3513
3536
  DOSSIER_NUMBER: "Skrárnúmer",
@@ -3545,6 +3568,7 @@ var SRP$8 = {
3545
3568
  TRAVEL_GROUP: "Ferðahópur",
3546
3569
  EXCURSION: "Útflutningur",
3547
3570
  LOADING_EXCURSIONS: "Hleðst inn í útflutninga...",
3571
+ LOADING_ACTIVITIES: "Hleð inn afþreyingu...",
3548
3572
  LOADING_FLIGHTS: "Hleðst inn í flug...",
3549
3573
  LOADING_ACCOMMODATIONS: "Hleðst inn í gistingar...",
3550
3574
  LOADING_ITINERARY: "Hleðst inn í ferðaráætlun...",
@@ -3952,6 +3976,8 @@ var SRP$7 = {
3952
3976
  DEPARTURE_ASC: "Data di partenza (prima la più vicina)",
3953
3977
  FILTERS: "Filtri",
3954
3978
  SHOW_ITINERARY: "Mostra itinerario",
3979
+ VIEW_FULL_ITINERARY: "Vedi l'itinerario completo",
3980
+ SHOW_SERVICES: "Mostra i servizi",
3955
3981
  VIEW_BOOKING: "Visualizza prenotazione",
3956
3982
  ITINERARY_TITLE: "Riepilogo del tuo viaggio",
3957
3983
  DOSSIER_NUMBER: "Numero di pratica",
@@ -3989,6 +4015,7 @@ var SRP$7 = {
3989
4015
  TRAVEL_GROUP: "Gruppo di viaggio",
3990
4016
  EXCURSION: "Escursione",
3991
4017
  LOADING_EXCURSIONS: "Caricamento delle escursioni...",
4018
+ LOADING_ACTIVITIES: "Caricamento delle attività...",
3992
4019
  LOADING_FLIGHTS: "Caricamento dei voli...",
3993
4020
  LOADING_ACCOMMODATIONS: "Caricamento degli alloggi...",
3994
4021
  LOADING_ITINERARY: "Caricamento dell'itinerario...",
@@ -4400,6 +4427,8 @@ var SRP$6 = {
4400
4427
  DEPARTURE_ASC: "Vertrekdatum (dichtstbij eerst)",
4401
4428
  FILTERS: "Filters",
4402
4429
  SHOW_ITINERARY: "Toon reisplan",
4430
+ VIEW_FULL_ITINERARY: "Bekijk volledige reisroute",
4431
+ SHOW_SERVICES: "Toon diensten",
4403
4432
  VIEW_BOOKING: "Bekijk boeking",
4404
4433
  ITINERARY_TITLE: "Overzicht van je reis",
4405
4434
  DOSSIER_NUMBER: "Dossiernummer",
@@ -4437,6 +4466,7 @@ var SRP$6 = {
4437
4466
  TRAVEL_GROUP: "Reisgezelschap",
4438
4467
  EXCURSION: "Excursie",
4439
4468
  LOADING_EXCURSIONS: "Excursies laden...",
4469
+ LOADING_ACTIVITIES: "Activiteiten laden...",
4440
4470
  LOADING_FLIGHTS: "Vluchten laden...",
4441
4471
  LOADING_ACCOMMODATIONS: "Accommodaties laden...",
4442
4472
  LOADING_ITINERARY: "Reisplan laden...",
@@ -4844,6 +4874,8 @@ var SRP$5 = {
4844
4874
  DEPARTURE_ASC: "Vertrekdatum (dichtstbij eerst)",
4845
4875
  FILTERS: "Filters",
4846
4876
  SHOW_ITINERARY: "Toon reisplan",
4877
+ VIEW_FULL_ITINERARY: "Bekijk volledige reisroute",
4878
+ SHOW_SERVICES: "Toon diensten",
4847
4879
  VIEW_BOOKING: "Bekijk boeking",
4848
4880
  ITINERARY_TITLE: "Overzicht van je reis",
4849
4881
  DOSSIER_NUMBER: "Dossiernummer",
@@ -4881,6 +4913,7 @@ var SRP$5 = {
4881
4913
  TRAVEL_GROUP: "Reisgezelschap",
4882
4914
  EXCURSION: "Excursie",
4883
4915
  LOADING_EXCURSIONS: "Excursies laden...",
4916
+ LOADING_ACTIVITIES: "Activiteiten laden...",
4884
4917
  LOADING_FLIGHTS: "Vluchten laden...",
4885
4918
  LOADING_ACCOMMODATIONS: "Accommodaties laden...",
4886
4919
  LOADING_ITINERARY: "Reisplan laden...",
@@ -5288,6 +5321,8 @@ var SRP$4 = {
5288
5321
  DEPARTURE_ASC: "Avreisedato (nærmeste først)",
5289
5322
  FILTERS: "Filtre",
5290
5323
  SHOW_ITINERARY: "Vis reiserute",
5324
+ VIEW_FULL_ITINERARY: "Se hele reiseruten",
5325
+ SHOW_SERVICES: "Vis tjenester",
5291
5326
  VIEW_BOOKING: "Se bestilling",
5292
5327
  ITINERARY_TITLE: "Din reisemåloversikt",
5293
5328
  DOSSIER_NUMBER: "Saksnummer",
@@ -5325,6 +5360,7 @@ var SRP$4 = {
5325
5360
  TRAVEL_GROUP: "Reisefølge",
5326
5361
  EXCURSION: "Utflukt",
5327
5362
  LOADING_EXCURSIONS: "Laster utflukter...",
5363
+ LOADING_ACTIVITIES: "Laster aktiviteter...",
5328
5364
  LOADING_FLIGHTS: "Laster flyvninger...",
5329
5365
  LOADING_ACCOMMODATIONS: "Laster overnattinger...",
5330
5366
  LOADING_ITINERARY: "Laster reiserute...",
@@ -5732,6 +5768,8 @@ var SRP$3 = {
5732
5768
  DEPARTURE_ASC: "Data wyjazdu (najbliższa najpierw)",
5733
5769
  FILTERS: "Filtry",
5734
5770
  SHOW_ITINERARY: "Pokaż plan podróży",
5771
+ VIEW_FULL_ITINERARY: "Zobacz pełny plan podróży",
5772
+ SHOW_SERVICES: "Pokaż usługi",
5735
5773
  VIEW_BOOKING: "Zobacz rezerwację",
5736
5774
  ITINERARY_TITLE: "Podsumowanie Twojej podróży",
5737
5775
  DOSSIER_NUMBER: "Numer pliku",
@@ -5769,6 +5807,7 @@ var SRP$3 = {
5769
5807
  TRAVEL_GROUP: "Grupa podróżnych",
5770
5808
  EXCURSION: "Wycieczka",
5771
5809
  LOADING_EXCURSIONS: "Ładowanie wycieczek...",
5810
+ LOADING_ACTIVITIES: "Ładowanie atrakcji...",
5772
5811
  LOADING_FLIGHTS: "Ładowanie lotów...",
5773
5812
  LOADING_ACCOMMODATIONS: "Ładowanie zakwaterowań...",
5774
5813
  LOADING_ITINERARY: "Ładowanie planu podróży...",
@@ -6176,6 +6215,8 @@ var SRP$2 = {
6176
6215
  DEPARTURE_ASC: "Data de partida (mais próxima primeiro)",
6177
6216
  FILTERS: "Filtros",
6178
6217
  SHOW_ITINERARY: "Mostrar itinerário",
6218
+ VIEW_FULL_ITINERARY: "Ver itinerário completo",
6219
+ SHOW_SERVICES: "Ver serviços",
6179
6220
  VIEW_BOOKING: "Ver reserva",
6180
6221
  ITINERARY_TITLE: "Resumo da sua viagem",
6181
6222
  DOSSIER_NUMBER: "Número do processo",
@@ -6213,6 +6254,7 @@ var SRP$2 = {
6213
6254
  TRAVEL_GROUP: "Grupo de viajantes",
6214
6255
  EXCURSION: "Excursão",
6215
6256
  LOADING_EXCURSIONS: "A carregar excursões...",
6257
+ LOADING_ACTIVITIES: "A carregar atividades...",
6216
6258
  LOADING_FLIGHTS: "A carregar voos...",
6217
6259
  LOADING_ACCOMMODATIONS: "A carregar alojamentos...",
6218
6260
  LOADING_ITINERARY: "A carregar itinerário...",
@@ -6620,6 +6662,8 @@ var SRP$1 = {
6620
6662
  DEPARTURE_ASC: "Avresedatum (närmast först)",
6621
6663
  FILTERS: "Filter",
6622
6664
  SHOW_ITINERARY: "Visa resplan",
6665
+ VIEW_FULL_ITINERARY: "Visa hela resplanen",
6666
+ SHOW_SERVICES: "Visa tjänster",
6623
6667
  VIEW_BOOKING: "Visa bokning",
6624
6668
  ITINERARY_TITLE: "Din resesammanfattning",
6625
6669
  DOSSIER_NUMBER: "Filnummer",
@@ -6657,6 +6701,7 @@ var SRP$1 = {
6657
6701
  TRAVEL_GROUP: "Resesällskap",
6658
6702
  EXCURSION: "Utflykt",
6659
6703
  LOADING_EXCURSIONS: "Laddar utflykter...",
6704
+ LOADING_ACTIVITIES: "Laddar aktiviteter...",
6660
6705
  LOADING_FLIGHTS: "Laddar flyg...",
6661
6706
  LOADING_ACCOMMODATIONS: "Laddar boenden...",
6662
6707
  LOADING_ITINERARY: "Laddar resplan...",
@@ -7064,6 +7109,8 @@ var SRP = {
7064
7109
  DEPARTURE_ASC: "出発日(早い順)",
7065
7110
  FILTERS: "フィルター",
7066
7111
  SHOW_ITINERARY: "旅程を表示",
7112
+ VIEW_FULL_ITINERARY: "旅程の詳細を見る",
7113
+ SHOW_SERVICES: "サービスを表示",
7067
7114
  VIEW_BOOKING: "予約を見る",
7068
7115
  ITINERARY_TITLE: "あなたの旅行概要",
7069
7116
  DOSSIER_NUMBER: "ファイル番号",
@@ -7101,6 +7148,7 @@ var SRP = {
7101
7148
  TRAVEL_GROUP: "旅行グループ",
7102
7149
  EXCURSION: "エクスカーション",
7103
7150
  LOADING_EXCURSIONS: "エクスカーションを読み込み中...",
7151
+ LOADING_ACTIVITIES: "アクティビティを読み込み中...",
7104
7152
  LOADING_FLIGHTS: "フライトを読み込み中...",
7105
7153
  LOADING_ACCOMMODATIONS: "宿泊施設を読み込み中...",
7106
7154
  LOADING_ITINERARY: "旅程を読み込み中...",
@@ -7772,6 +7820,33 @@ var TrajectNodeType = {
7772
7820
  subroute: 3,
7773
7821
  };
7774
7822
 
7823
+ var DurationType = {
7824
+ hours: 0,
7825
+ days: 1,
7826
+ nights: 2,
7827
+ minutes: 3,
7828
+ };
7829
+
7830
+ var RouteTransportType = {
7831
+ none: 0,
7832
+ privateTransport: 1,
7833
+ publicTransport: 2,
7834
+ rentalCar: 3,
7835
+ bus: 4,
7836
+ camper: 5,
7837
+ car: 6,
7838
+ charter: 7,
7839
+ cruise: 8,
7840
+ ferry: 9,
7841
+ flight: 10,
7842
+ taxi: 11,
7843
+ train: 12,
7844
+ transfer: 13,
7845
+ rocket: 14,
7846
+ walking: 15,
7847
+ cycling: 16,
7848
+ };
7849
+
7775
7850
  /******************************************************************************
7776
7851
  Copyright (c) Microsoft Corporation.
7777
7852
 
@@ -15123,6 +15198,7 @@ var submitForm = function (config, id, request, signal) {
15123
15198
  exports.AllotmentType = AllotmentType;
15124
15199
  exports.DataType = DataType;
15125
15200
  exports.DossierType = DossierType;
15201
+ exports.DurationType = DurationType;
15126
15202
  exports.EntryLineFlag = EntryLineFlag;
15127
15203
  exports.EntryLineStatus = EntryLineStatus;
15128
15204
  exports.ExportStatus = ExportStatus;
@@ -15135,6 +15211,7 @@ exports.PaymentType = PaymentType;
15135
15211
  exports.PriceChartType = PriceChartType;
15136
15212
  exports.RequirementType = RequirementType;
15137
15213
  exports.RoomType = RoomType;
15214
+ exports.RouteTransportType = RouteTransportType;
15138
15215
  exports.SaleType = SaleType;
15139
15216
  exports.ServiceTokenError = ServiceTokenError;
15140
15217
  exports.ServiceType = ServiceType;
@@ -16612,6 +16689,16 @@ const Icon = ({ name, className, title, width, height, fill }) => {
16612
16689
  React__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." }),
16613
16690
  title && React__default.createElement("title", null, title),
16614
16691
  React__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" })));
16692
+ case 'ui-cycling':
16693
+ return (React__default.createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !isEmpty$1(className)).join(' '), width: width, height: height, viewBox: "0 0 640 512", fill: fill ?? 'currentColor' },
16694
+ React__default.createElement(HTMLComment, { text: "!Font Awesome Free v5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc." }),
16695
+ title && React__default.createElement("title", null, title),
16696
+ React__default.createElement("path", { d: "M512.509 192.001c-16.373-.064-32.03 2.955-46.436 8.495l-77.68-125.153A24 24 0 0 0 368.001 64h-64c-8.837 0-16 7.163-16 16v16c0 8.837 7.163 16 16 16h50.649l14.896 24H256.002v-16c0-8.837-7.163-16-16-16h-87.459c-13.441 0-24.777 10.999-24.536 24.437.232 13.044 10.876 23.563 23.995 23.563h48.726l-29.417 47.52c-13.433-4.83-27.904-7.483-42.992-7.52C58.094 191.83.412 249.012.002 319.236-.413 390.279 57.055 448 128.002 448c59.642 0 109.758-40.793 123.967-96h52.033a24 24 0 0 0 20.406-11.367L410.37 201.77l14.938 24.067c-25.455 23.448-41.385 57.081-41.307 94.437.145 68.833 57.899 127.051 126.729 127.719 70.606.685 128.181-55.803 129.255-125.996 1.086-70.941-56.526-129.72-127.476-129.996zM186.75 265.772c9.727 10.529 16.673 23.661 19.642 38.228h-43.306l23.664-38.228zM128.002 400c-44.112 0-80-35.888-80-80s35.888-80 80-80c5.869 0 11.586.653 17.099 1.859l-45.505 73.509C89.715 331.327 101.213 352 120.002 352h81.3c-12.37 28.225-40.562 48-73.3 48zm162.63-96h-35.624c-3.96-31.756-19.556-59.894-42.383-80.026L237.371 184h127.547l-74.286 120zm217.057 95.886c-41.036-2.165-74.049-35.692-75.627-76.755-.812-21.121 6.633-40.518 19.335-55.263l44.433 71.586c4.66 7.508 14.524 9.816 22.032 5.156l13.594-8.437c7.508-4.66 9.817-14.524 5.156-22.032l-44.468-71.643a79.901 79.901 0 0 1 19.858-2.497c44.112 0 80 35.888 80 80-.001 45.54-38.252 82.316-84.313 79.885z", fill: "currentColor" })));
16697
+ case 'ui-walking':
16698
+ return (React__default.createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !isEmpty$1(className)).join(' '), width: width, height: height, viewBox: "0 0 320 512", fill: fill ?? 'currentColor' },
16699
+ React__default.createElement(HTMLComment, { text: "!Font Awesome Free v5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc." }),
16700
+ title && React__default.createElement("title", null, title),
16701
+ React__default.createElement("path", { d: "M208 96c26.5 0 48-21.5 48-48S234.5 0 208 0s-48 21.5-48 48 21.5 48 48 48zm94.5 149.1l-23.3-11.8-9.7-29.4c-14.7-44.6-55.7-75.8-102.2-75.9-36-.1-55.9 10.1-93.3 25.2-21.6 8.7-39.3 25.2-49.7 46.2L17.6 213c-7.8 15.8-1.5 35 14.2 42.9 15.6 7.9 34.6 1.5 42.5-14.3L81 228c3.5-7 9.3-12.5 16.5-15.4l26.8-10.8-15.2 60.7c-5.2 20.8.4 42.9 14.9 58.8l59.9 65.4c7.2 7.9 12.3 17.4 14.9 27.7l18.3 73.3c4.3 17.1 21.7 27.6 38.8 23.3 17.1-4.3 27.6-21.7 23.3-38.8l-22.2-89c-2.6-10.3-7.7-19.9-14.9-27.7l-45.5-49.7 17.2-68.7 5.5 16.5c5.3 16.1 16.7 29.4 31.7 37l23.3 11.8c15.6 7.9 34.6 1.5 42.5-14.3 7.7-15.7 1.4-35.1-14.3-43zM73.6 385.8c-3.2 8.1-8 15.4-14.2 21.5l-50 50.1c-12.5 12.5-12.5 32.8 0 45.3s32.7 12.5 45.2 0l59.4-59.4c6.1-6.1 10.9-13.4 14.2-21.5l13.5-33.8c-55.3-60.3-38.7-41.8-47.4-53.7l-20.7 51.5z", fill: "currentColor" })));
16615
16702
  case 'ui-bus':
16616
16703
  return (React__default.createElement("svg", { className: ['icon', `icon--${name}`, className].filter((className) => !isEmpty$1(className)).join(' '), width: width, height: height, viewBox: "0 0 512 512", fill: fill ?? 'currentColor' },
16617
16704
  title && React__default.createElement("title", null, title),
@@ -16925,10 +17012,10 @@ const DateRangePicker$1 = (props) => {
16925
17012
  month: getMonth(new Date())
16926
17013
  });
16927
17014
  const [isWaitingForToDate, setWaitingForToDate] = useState(false);
16928
- const { searchType = 0 } = useContext(SettingsContext$1);
17015
+ const { searchType = 0, allowSingleDayDateSelect = false } = useContext(SettingsContext$1);
16929
17016
  const handleDayClick = (day) => {
16930
17017
  const { onSelectionChange } = props;
16931
- if (isWaitingForToDate && !isNil(fromDate) && isAfter(day, fromDate)) {
17018
+ if (isWaitingForToDate && !isNil(fromDate) && (isAfter(day, fromDate) || (allowSingleDayDateSelect && isSameDay(day, fromDate)))) {
16932
17019
  if (searchType === 1) {
16933
17020
  // Only allow selecting the exact toDate
16934
17021
  const expectedToDate = props.toDateByFromDate?.(fromDate);
@@ -16969,7 +17056,7 @@ const DateRangePicker$1 = (props) => {
16969
17056
  }
16970
17057
  };
16971
17058
  const handleDayMouseOver = (day) => {
16972
- if (isWaitingForToDate && (isNil(fromDate) || isEqual(day, fromDate) || isAfter(day, fromDate))) {
17059
+ if (isWaitingForToDate && (isNil(fromDate) || isSameDay(day, fromDate) || isAfter(day, fromDate))) {
16973
17060
  setToDate(day);
16974
17061
  }
16975
17062
  };
@@ -27442,7 +27529,9 @@ const initialState$1 = {
27442
27529
  trajectFlightAvailability: {},
27443
27530
  trajectDaySuggestions: {},
27444
27531
  trajectEditLineGuid: null,
27445
- trajectActivitiesDayOrder: null
27532
+ trajectActivitiesDayOrder: null,
27533
+ trajectResolving: false,
27534
+ trajectItineraryView: false
27446
27535
  };
27447
27536
  const searchResultsSlice = createSlice({
27448
27537
  name: 'searchResults',
@@ -27712,6 +27801,12 @@ const searchResultsSlice = createSlice({
27712
27801
  setTrajectActivitiesDayOrder(state, action) {
27713
27802
  state.trajectActivitiesDayOrder = action.payload;
27714
27803
  },
27804
+ setTrajectResolving(state, action) {
27805
+ state.trajectResolving = action.payload;
27806
+ },
27807
+ setTrajectItineraryView(state, action) {
27808
+ state.trajectItineraryView = action.payload;
27809
+ },
27715
27810
  updateTrajectNodeResult(state, action) {
27716
27811
  const { lineGuid, result } = action.payload;
27717
27812
  const node = state.trajectNodeAvailability[lineGuid];
@@ -27727,10 +27822,12 @@ const searchResultsSlice = createSlice({
27727
27822
  state.trajectDaySuggestions = {};
27728
27823
  state.trajectEditLineGuid = null;
27729
27824
  state.trajectActivitiesDayOrder = null;
27825
+ state.trajectResolving = false;
27826
+ state.trajectItineraryView = false;
27730
27827
  }
27731
27828
  }
27732
27829
  });
27733
- 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, setTrajectActivitiesDayOrder, setTrajectDaySuggestionsLoading, setTrajectDaySuggestions, addEditableEntryLines, updateTrajectNodeResult, resetTrajectAvailability, updateEditableEntryLine, removeEditableEntryLines } = searchResultsSlice.actions;
27830
+ 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;
27734
27831
  var searchResultsReducer = searchResultsSlice.reducer;
27735
27832
 
27736
27833
  const ItemPicker = ({ items, selection, selectedSortByType, label, placeholder, classModifier, onPick, valueFormatter }) => {
@@ -27978,8 +28075,97 @@ const getServiceTypeBadgeModifier = (serviceType) => {
27978
28075
  return '';
27979
28076
  }
27980
28077
  };
28078
+ // DurationType, as the SDK numbers it.
28079
+ const DURATION_HOURS = 0;
28080
+ const DURATION_DAYS = 1;
28081
+ const DURATION_NIGHTS = 2;
28082
+ const DURATION_MINUTES = 3;
28083
+ /**
28084
+ * How long a node takes, in the same shape the flight legs are written in — an hour count only
28085
+ * once there is one, so a short drive reads as plain minutes.
28086
+ */
28087
+ const getTrajectNodeDuration = (node) => {
28088
+ const minutes = toMinutes(node);
28089
+ if (!minutes)
28090
+ return '';
28091
+ const hours = Math.floor(minutes / 60);
28092
+ const rest = minutes % 60;
28093
+ return hours ? `${hours}h ${rest.toString().padStart(2, '0')}m` : `${rest} min`;
28094
+ };
28095
+ const toMinutes = (node) => {
28096
+ const duration = node.duration ?? 0;
28097
+ if (duration <= 0)
28098
+ return 0;
28099
+ switch (node.durationType) {
28100
+ case DURATION_MINUTES:
28101
+ return duration;
28102
+ case DURATION_HOURS:
28103
+ return duration * 60;
28104
+ case DURATION_DAYS:
28105
+ case DURATION_NIGHTS:
28106
+ return duration * 24 * 60;
28107
+ default:
28108
+ return 0;
28109
+ }
28110
+ };
28111
+ // RouteTransportType, as the SDK numbers it. It says how a leg is travelled, which is not the
28112
+ // same question as what was sold: cycling and walking have no service type of their own.
28113
+ const TRANSPORT_PRIVATE = 1;
28114
+ const TRANSPORT_PUBLIC = 2;
28115
+ const TRANSPORT_RENTAL_CAR = 3;
28116
+ const TRANSPORT_BUS = 4;
28117
+ const TRANSPORT_CAMPER = 5;
28118
+ const TRANSPORT_CAR = 6;
28119
+ const TRANSPORT_CHARTER = 7;
28120
+ const TRANSPORT_CRUISE = 8;
28121
+ const TRANSPORT_FERRY = 9;
28122
+ const TRANSPORT_FLIGHT = 10;
28123
+ const TRANSPORT_TAXI = 11;
28124
+ const TRANSPORT_TRAIN = 12;
28125
+ const TRANSPORT_TRANSFER = 13;
28126
+ const TRANSPORT_WALKING = 15;
28127
+ const TRANSPORT_CYCLING = 16;
28128
+ /** Icon per way of travelling, where the route records one. */
28129
+ const getTransportTypeIcon = (transportType) => {
28130
+ switch (transportType) {
28131
+ case TRANSPORT_WALKING:
28132
+ return 'ui-walking';
28133
+ case TRANSPORT_CYCLING:
28134
+ return 'ui-cycling';
28135
+ case TRANSPORT_PRIVATE:
28136
+ case TRANSPORT_RENTAL_CAR:
28137
+ case TRANSPORT_CAMPER:
28138
+ case TRANSPORT_CAR:
28139
+ return 'ui-car';
28140
+ case TRANSPORT_TAXI:
28141
+ case TRANSPORT_TRANSFER:
28142
+ return 'ui-transfer';
28143
+ case TRANSPORT_PUBLIC:
28144
+ case TRANSPORT_BUS:
28145
+ return 'ui-bus';
28146
+ case TRANSPORT_TRAIN:
28147
+ return 'ui-train';
28148
+ case TRANSPORT_CRUISE:
28149
+ case TRANSPORT_FERRY:
28150
+ return 'ui-ship';
28151
+ case TRANSPORT_FLIGHT:
28152
+ case TRANSPORT_CHARTER:
28153
+ return 'ui-plane';
28154
+ default:
28155
+ return null;
28156
+ }
28157
+ };
27981
28158
  const POI_TRAJECT_NODE_TYPE = 2;
27982
- const getTrajectNodeIcon = (node) => node.trajectNodeType === POI_TRAJECT_NODE_TYPE ? 'ui-location' : getServiceTypeIcon(node.serviceType);
28159
+ /**
28160
+ * A point of interest is a place on the map rather than a service, so it gets a marker. Anything
28161
+ * else goes by how it is travelled where the route says so — a leg on foot or by bike is sold as
28162
+ * nothing, and its service type falls back to a transfer — and by what it is where it does not.
28163
+ */
28164
+ const getTrajectNodeIcon = (node) => {
28165
+ if (node.trajectNodeType === POI_TRAJECT_NODE_TYPE)
28166
+ return 'ui-location';
28167
+ return getTransportTypeIcon(node.transportType) ?? getServiceTypeIcon(node.serviceType);
28168
+ };
27983
28169
  const getTrajectNodeBadgeModifier = (node) => node.trajectNodeType === POI_TRAJECT_NODE_TYPE ? 'poi' : getServiceTypeBadgeModifier(node.serviceType);
27984
28170
 
27985
28171
  const REGULAR_TRAJECT_NODE_TYPE = 0;
@@ -28020,7 +28206,22 @@ const buildDestination = (line, node, result) => {
28020
28206
  null;
28021
28207
  return { id: 0, ...place, ...coordinates };
28022
28208
  };
28023
- const buildTrajectNodeRequest = ({ node, line }, context, productCode) => ({
28209
+ const hasDestination = (destination) => !!destination && (!!destination.id || (!!destination.latitude && !!destination.longitude));
28210
+ const buildTrajectClientConfig = (context) => ({
28211
+ host: context.tideConnection.host,
28212
+ apiKey: context.tideConnection.apiKey
28213
+ });
28214
+ const buildTrajectSearchContext = (context, trajectEntry) => ({
28215
+ transactionId: trajectEntry.entry.transactionId,
28216
+ officeId: context.tideConnection.officeId ?? 1,
28217
+ catalogueId: context.searchConfiguration.defaultCatalogueId ?? context.tideConnection.catalogueIds?.[0] ?? 1,
28218
+ searchConfigurationId: context.searchConfiguration.id,
28219
+ portalId: context.portalId ?? null,
28220
+ agentId: context.agentId ?? null,
28221
+ language: context.languageCode ?? 'en-GB',
28222
+ rooms: getPackagingRoomsFromEntry(trajectEntry.entry)
28223
+ });
28224
+ const buildTrajectNodeRequest = ({ node, line }, context, productCode, destination) => ({
28024
28225
  transactionId: context.transactionId,
28025
28226
  officeId: context.officeId,
28026
28227
  agentId: context.agentId ?? null,
@@ -28031,25 +28232,69 @@ const buildTrajectNodeRequest = ({ node, line }, context, productCode) => ({
28031
28232
  language: context.language,
28032
28233
  serviceType: node.serviceType,
28033
28234
  fromDate: toDateOnlyString(line.from),
28034
- toDate: toDateOnlyString(line.to),
28035
- destination: buildDestination(line, node),
28235
+ // An excursion happens on a day rather than over a span. The traject gives its line an end a day
28236
+ // out, the way a night is counted, which asks the search for a window it does not have.
28237
+ toDate: toDateOnlyString(node.serviceType === EXCURSION_SERVICE_TYPE ? line.from : line.to),
28238
+ destination: destination ?? buildDestination(line, node),
28036
28239
  productCode,
28037
28240
  rooms: context.rooms,
28038
28241
  tagIds: []
28039
28242
  });
28040
- /**
28041
- * Entry lines for an excursion the traveller picked out of the suggestions.
28042
- *
28043
- * A traject node already has its lines and only needs stamping; a suggestion has none, so this
28044
- * builds them — one per selected option, as the regular day-by-day excursion flow does, since an
28045
- * excursion's options are ticket types (adult, child) rather than rooms. Where the search marks no
28046
- * default, the first option of each room stands in.
28047
- */
28048
- const buildTrajectExcursionLines = (result) => {
28049
- const options = (result.rooms ?? []).flatMap((room) => {
28050
- const selected = (room.options ?? []).filter((x) => x.isSelected);
28051
- return selected.length ? selected : (room.options ?? []).slice(0, 1);
28243
+ const coversDay = ({ node, line }, dayOrder) => {
28244
+ const nights = Math.max(differenceInCalendarDays(new Date(line.to), new Date(line.from)), 1);
28245
+ return dayOrder >= node.dayOrder && dayOrder < node.dayOrder + nights;
28246
+ };
28247
+ const getDayDestinationItem = (items, dayOrder) => {
28248
+ const knowsWhereItIs = (x) => hasDestination(buildDestination(x.line, x.node));
28249
+ const stay = items.find((x) => x.node.serviceType === ACCOMMODATION_SERVICE_TYPE && coversDay(x, dayOrder) && knowsWhereItIs(x));
28250
+ if (stay)
28251
+ return stay;
28252
+ return ([...items]
28253
+ .filter((x) => x.node.dayOrder <= dayOrder && knowsWhereItIs(x))
28254
+ .sort((a, b) => a.node.dayOrder - b.node.dayOrder || a.node.order - b.node.order)
28255
+ .pop() ?? null);
28256
+ };
28257
+ const getDayDestination = (items, availability, dayOrder) => {
28258
+ const item = getDayDestinationItem(items, dayOrder);
28259
+ if (!item)
28260
+ return null;
28261
+ const resolved = availability[item.line.guid];
28262
+ const result = resolved?.results.find((x) => x.code === resolved.selectedCode) ?? null;
28263
+ const destination = buildDestination(item.line, item.node, result);
28264
+ return hasDestination(destination) ? destination : null;
28265
+ };
28266
+ const buildTrajectDayExcursionRequest = (date, destination, context) => ({
28267
+ transactionId: context.transactionId,
28268
+ officeId: context.officeId,
28269
+ agentId: context.agentId ?? null,
28270
+ catalogueId: context.catalogueId,
28271
+ searchConfigurationId: context.searchConfigurationId,
28272
+ portalId: context.portalId ?? null,
28273
+ vendorConfigurationId: null,
28274
+ language: context.language,
28275
+ serviceType: EXCURSION_SERVICE_TYPE,
28276
+ fromDate: toDateOnlyString(date),
28277
+ toDate: toDateOnlyString(date),
28278
+ destination,
28279
+ productCode: '',
28280
+ rooms: context.rooms,
28281
+ tagIds: []
28282
+ });
28283
+ const getExcursionOptionGroups = (result) => {
28284
+ const groups = new Map();
28285
+ (result.rooms ?? [])
28286
+ .flatMap((room) => room.options ?? [])
28287
+ .forEach((option) => {
28288
+ const key = [...(option.paxIds ?? [])].sort((a, b) => a - b).join(',');
28289
+ groups.set(key, [...(groups.get(key) ?? []), option]);
28052
28290
  });
28291
+ return [...groups.values()];
28292
+ };
28293
+ const excursionNeedsChoice = (result) => getExcursionOptionGroups(result).some((group) => group.length > 1 && !group.some((option) => option.isSelected));
28294
+ const buildTrajectExcursionLines = (result) => {
28295
+ const options = getExcursionOptionGroups(result)
28296
+ .map((group) => group.find((x) => x.isSelected) ?? (group.length === 1 ? group[0] : null))
28297
+ .filter((x) => !!x);
28053
28298
  if (!options.length)
28054
28299
  return [];
28055
28300
  const guids = options.map((option) => option.guid || crypto.randomUUID());
@@ -28078,6 +28323,21 @@ const buildTrajectExcursionLines = (result) => {
28078
28323
  flightInformation: null
28079
28324
  }));
28080
28325
  };
28326
+ const buildExcursionSearchParams = (result) => ({
28327
+ date: result.fromDate,
28328
+ fromDate: result.fromDate,
28329
+ toDate: result.toDate,
28330
+ countryId: result.countryId,
28331
+ regionId: result.regionId,
28332
+ oordId: result.oordId,
28333
+ locationId: result.locationId,
28334
+ locationName: result.locationName,
28335
+ accommodationCode: null,
28336
+ accommodationName: null,
28337
+ hotelCode: result.code,
28338
+ hotelName: result.name,
28339
+ rooms: result.rooms ?? []
28340
+ });
28081
28341
  const buildDayActivityCards = (items, availability, suggestions) => {
28082
28342
  const cards = [];
28083
28343
  const seen = new Set();
@@ -28094,11 +28354,27 @@ const buildDayActivityCards = (items, availability, suggestions) => {
28094
28354
  results.filter((x) => !!findCandidateForResult(item.node, x)).forEach((result) => push(result, item));
28095
28355
  });
28096
28356
  suggestions.forEach((result) => push(result));
28097
- // Whatever the nodes' own destination search found and the day's search did not — normally
28098
- // nothing, as they are the same search, but a node can resolve where a day has no place to look.
28099
- items.forEach((item) => (availability[item.line.guid]?.results ?? []).forEach((result) => push(result, item)));
28357
+ items.forEach((item) => (availability[item.line.guid]?.results ?? []).forEach((result) => push(result)));
28100
28358
  return cards;
28101
28359
  };
28360
+ /**
28361
+ * Where to search for a node.
28362
+ *
28363
+ * Normally the node says so itself, through the product it was configured with. An excursion slot
28364
+ * left empty on the traject says nothing at all — no code, no alternatives, and a line with no
28365
+ * place on it — so it falls back to wherever the party is staying that day, the same anchor the
28366
+ * day's own activity search uses.
28367
+ */
28368
+ const getNodeSearchDestination = (items, item) => {
28369
+ const own = buildDestination(item.line, item.node);
28370
+ if (hasDestination(own))
28371
+ return own;
28372
+ return getDayDestination(items, {}, item.node.dayOrder) ?? own;
28373
+ };
28374
+ const getConfiguredExcursionCodes = (items, dayOrder) => new Set(items
28375
+ .filter((x) => x.node.dayOrder === dayOrder && x.node.serviceType === EXCURSION_SERVICE_TYPE)
28376
+ .flatMap((x) => [x.node.code, ...getNodeCandidates(x.node).map((candidate) => candidate.code)])
28377
+ .filter((code) => !!code));
28102
28378
  const getNodeCandidates = (node) => {
28103
28379
  const configured = (node.alternatives ?? []).filter((x) => !!x.code && x.code.trim().length > 0);
28104
28380
  if (configured.length)
@@ -28207,6 +28483,51 @@ const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
28207
28483
  outward: { date: dateToDateStruct(new Date(line.from)) },
28208
28484
  return: null
28209
28485
  });
28486
+ /**
28487
+ * The entry as the backend can price it.
28488
+ *
28489
+ * A search caches an EntryLine per option and hands back that line's guid on the option. The
28490
+ * backend then rebuilds the entry by looking each line up in that cache on its guid, so a line
28491
+ * it cannot find is dropped — PackagingEntryBuilder.SyncEntryLines skips it, and an entry whose
28492
+ * lines all came off a traject prices as nothing at all.
28493
+ *
28494
+ * A traject entry is rendered before any search runs, so its lines carry the guids the traject
28495
+ * mapper gave them. This swaps in the guid of whatever each line resolved onto, leaving the
28496
+ * lines the traveller added themselves alone — those were built from a search result and already
28497
+ * carry the right one.
28498
+ */
28499
+ const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightAvailability) => {
28500
+ const cachedGuids = new Map();
28501
+ getTrajectNodesWithLines(trajectEntry).forEach((item) => {
28502
+ if (isTrajectFlightNode(item.node)) {
28503
+ // A flight search caches the outward line under the guid it reports as outwardGuid.
28504
+ const guid = flightAvailability[item.line.guid]?.selectedGuid;
28505
+ if (guid)
28506
+ cachedGuids.set(item.line.guid, guid);
28507
+ return;
28508
+ }
28509
+ const availability = nodeAvailability[item.line.guid];
28510
+ const result = availability?.results.find((x) => x.code === availability.selectedCode);
28511
+ if (!result)
28512
+ return;
28513
+ // Room i took option i when the result was stamped on, so it takes option i here too.
28514
+ item.lines.forEach((line, index) => {
28515
+ const guid = getOptionForRoom(result, index)?.guid;
28516
+ if (guid)
28517
+ cachedGuids.set(line.guid, guid);
28518
+ });
28519
+ });
28520
+ if (!cachedGuids.size)
28521
+ return entry;
28522
+ return {
28523
+ ...entry,
28524
+ lines: (entry.lines ?? []).map((line) => ({
28525
+ ...line,
28526
+ guid: cachedGuids.get(line.guid) ?? line.guid,
28527
+ parentGuid: (line.parentGuid && cachedGuids.get(line.parentGuid)) ?? line.parentGuid
28528
+ }))
28529
+ };
28530
+ };
28210
28531
  const pickCheapestFlight = (flights) => [...flights].sort((a, b) => a.price - b.price)[0] ?? null;
28211
28532
  const applyFlightToLine = (line, flight) => {
28212
28533
  const segments = flight.outward?.segments ?? [];
@@ -28283,6 +28604,8 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
28283
28604
  trajectNodeType: REGULAR_TRAJECT_NODE_TYPE,
28284
28605
  requirementType: 0,
28285
28606
  contentSource: 0,
28607
+ duration: 0,
28608
+ durationType: 0,
28286
28609
  alternatives: [],
28287
28610
  from: line.from,
28288
28611
  to: line.to
@@ -28316,6 +28639,8 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
28316
28639
  React__default.createElement("p", null, format$2(new Date(value), 'MMM'))));
28317
28640
  const renderConnectingNode = ({ node }) => {
28318
28641
  const distance = node.distanceInKm ? `${Math.round(node.distanceInKm)} km` : '';
28642
+ // How long the hop takes and how far it is, the way the traject editor puts them.
28643
+ const journey = [getTrajectNodeDuration(node), distance].filter(Boolean).join(' · ');
28319
28644
  return (React__default.createElement("div", { className: "search__filter__itinerary__transport", key: `connect-${node.nodeId}` },
28320
28645
  React__default.createElement("div", { className: "search__filter__itinerary__transport-timeline" },
28321
28646
  React__default.createElement("div", { className: "search__filter__itinerary__transport-timeline-line" })),
@@ -28324,8 +28649,8 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
28324
28649
  React__default.createElement("div", { className: badgeClass('transport', getTrajectNodeBadgeModifier(node)) },
28325
28650
  React__default.createElement(Icon, { name: getTrajectNodeIcon(node), height: 15 })),
28326
28651
  React__default.createElement("div", { className: "search__filter__itinerary__transport-details" },
28327
- React__default.createElement("h6", null, node.name || distance),
28328
- !!node.name && !!distance && React__default.createElement("p", { className: "search__filter__itinerary__transport-details-time" }, distance)))));
28652
+ React__default.createElement("h6", null, node.name || journey),
28653
+ !!node.name && !!journey && React__default.createElement("p", { className: "search__filter__itinerary__transport-details-time" }, journey)))));
28329
28654
  };
28330
28655
  const renderOvernight = (item, index) => {
28331
28656
  const { dayOrder } = item.node;
@@ -28469,7 +28794,7 @@ const getServiceTypePriority = (serviceType) => {
28469
28794
  const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation }) => {
28470
28795
  const context = useContext(SearchResultsConfigurationContext);
28471
28796
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
28472
- const { editablePackagingEntry, priceDetails } = useSelector((state) => state.searchResults);
28797
+ const { editablePackagingEntry, priceDetails, trajectItineraryView } = useSelector((state) => state.searchResults);
28473
28798
  const dispatch = useDispatch();
28474
28799
  const packagingEntry = editablePackagingEntry ?? context?.packagingEntry;
28475
28800
  const sortedLines = useMemo(() => {
@@ -28512,6 +28837,11 @@ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation })
28512
28837
  const handleConfirm = () => {
28513
28838
  dispatch(setBookPackagingEntry(true));
28514
28839
  };
28840
+ const handleTrajectView = (itinerary) => {
28841
+ dispatch(setTrajectItineraryView(itinerary));
28842
+ if (typeof window !== 'undefined')
28843
+ window.scrollTo(0, 0);
28844
+ };
28515
28845
  return (React__default.createElement("div", { className: `search__filters--modal ${isOpen ? 'is-open' : ''}` },
28516
28846
  React__default.createElement("div", { className: "search__filters--background", onClick: handleSetIsOpen }),
28517
28847
  React__default.createElement("button", { className: "search__filters--close", onClick: handleSetIsOpen },
@@ -28539,7 +28869,8 @@ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation })
28539
28869
  " ",
28540
28870
  translations.SRP.TOTAL,
28541
28871
  ")"))),
28542
- React__default.createElement("button", { className: "cta", onClick: handleConfirm }, translations.QSM.CONFIRM)))),
28872
+ React__default.createElement("button", { className: "cta", onClick: handleConfirm }, translations.QSM.CONFIRM),
28873
+ !!context?.trajectEntry && (React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleTrajectView(!trajectItineraryView) }, trajectItineraryView ? translations.SRP.SHOW_SERVICES : translations.SRP.VIEW_FULL_ITINERARY))))),
28543
28874
  React__default.createElement("div", { className: "search__filter__itinerary" },
28544
28875
  React__default.createElement("p", null, translations.SRP.DAY_BY_DAY),
28545
28876
  firstEntryLine && (React__default.createElement("div", { className: "search__filter__itinerary__country" },
@@ -31033,10 +31364,9 @@ const getOptionPaxIds = (option) => {
31033
31364
  const optionAppliesToPax = (option, paxId) => {
31034
31365
  return getOptionPaxIds(option).includes(paxId);
31035
31366
  };
31036
- const optionAppliesToAllTravellers = (option, travellerCount) => {
31367
+ const optionAppliesToAllTravellers = (option, travellerIds) => {
31037
31368
  const paxIds = getOptionPaxIds(option);
31038
- const expected = Array.from({ length: travellerCount }, (_, i) => i);
31039
- return paxIds.length === expected.length && paxIds.every((id, index) => id === expected[index]);
31369
+ return paxIds.length === travellerIds.length && travellerIds.every((id) => paxIds.includes(id));
31040
31370
  };
31041
31371
  const groupOptionsByExcursion = (options) => {
31042
31372
  const groupedMap = new Map();
@@ -31061,26 +31391,26 @@ const ExcursionDetails = () => {
31061
31391
  return null;
31062
31392
  }
31063
31393
  const translations = getTranslations(context.languageCode ?? 'en-GB');
31064
- const travellerCount = editablePackagingEntry.pax.length;
31394
+ const travellerIds = editablePackagingEntry.pax.map((pax) => pax.id);
31065
31395
  const allOptions = useMemo(() => {
31066
31396
  return selectedExcursionSearchResult.rooms.flatMap((room) => room.options ?? []);
31067
31397
  }, [selectedExcursionSearchResult]);
31068
31398
  const sharedOptions = useMemo(() => {
31069
- return allOptions.filter((option) => optionAppliesToAllTravellers(option, travellerCount));
31070
- }, [allOptions, travellerCount]);
31399
+ return allOptions.filter((option) => optionAppliesToAllTravellers(option, travellerIds));
31400
+ }, [allOptions, travellerIds]);
31071
31401
  const sharedExcursions = useMemo(() => {
31072
31402
  return groupOptionsByExcursion(sharedOptions);
31073
31403
  }, [sharedOptions]);
31074
31404
  const paxGroups = useMemo(() => {
31075
31405
  return editablePackagingEntry.pax.map((pax) => {
31076
- const paxOptions = allOptions.filter((option) => optionAppliesToPax(option, pax.id) && !optionAppliesToAllTravellers(option, travellerCount));
31406
+ const paxOptions = allOptions.filter((option) => optionAppliesToPax(option, pax.id) && !optionAppliesToAllTravellers(option, travellerIds));
31077
31407
  return {
31078
31408
  pax,
31079
31409
  paxId: pax.id,
31080
31410
  excursions: groupOptionsByExcursion(paxOptions)
31081
31411
  };
31082
31412
  });
31083
- }, [editablePackagingEntry.pax, allOptions, travellerCount]);
31413
+ }, [editablePackagingEntry.pax, allOptions, travellerIds]);
31084
31414
  const getSelectedSharedOption = () => {
31085
31415
  return sharedOptions.find((option) => option.isSelected);
31086
31416
  };
@@ -31088,11 +31418,11 @@ const ExcursionDetails = () => {
31088
31418
  return sharedOptions.find((option) => option.accommodationCode === accommodationCode && option.isSelected);
31089
31419
  };
31090
31420
  const getSelectedOptionForPax = (paxId) => {
31091
- return allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerCount) && option.isSelected);
31421
+ return allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerIds) && option.isSelected);
31092
31422
  };
31093
31423
  const getSelectedOptionForExcursion = (paxId, accommodationCode) => {
31094
31424
  return allOptions.find((option) => optionAppliesToPax(option, paxId) &&
31095
- !optionAppliesToAllTravellers(option, travellerCount) &&
31425
+ !optionAppliesToAllTravellers(option, travellerIds) &&
31096
31426
  option.accommodationCode === accommodationCode &&
31097
31427
  option.isSelected);
31098
31428
  };
@@ -31102,7 +31432,7 @@ const ExcursionDetails = () => {
31102
31432
  rooms: selectedExcursionSearchResult.rooms.map((room) => ({
31103
31433
  ...room,
31104
31434
  options: room.options.map((option) => {
31105
- const isSharedOption = optionAppliesToAllTravellers(option, travellerCount);
31435
+ const isSharedOption = optionAppliesToAllTravellers(option, travellerIds);
31106
31436
  if (paxId === undefined) {
31107
31437
  if (!isSharedOption) {
31108
31438
  return option;
@@ -31149,7 +31479,7 @@ const ExcursionDetails = () => {
31149
31479
  targetPrice = selectedOption.price;
31150
31480
  }
31151
31481
  else {
31152
- const firstOption = allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerCount) && option.accommodationCode === accommodationCode);
31482
+ const firstOption = allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerIds) && option.accommodationCode === accommodationCode);
31153
31483
  targetPrice = firstOption?.price || 0;
31154
31484
  }
31155
31485
  return targetPrice - (currentSelectedPrice || 0);
@@ -31171,6 +31501,13 @@ const ExcursionDetails = () => {
31171
31501
  return 'flyin__acco__price';
31172
31502
  };
31173
31503
  const handleConfirm = () => {
31504
+ if (context.trajectEntry) {
31505
+ const lines = buildTrajectExcursionLines(selectedExcursionSearchResult);
31506
+ if (lines.length)
31507
+ dispatch(addEditableEntryLines(lines));
31508
+ dispatch(setFlyInIsOpen(false));
31509
+ return;
31510
+ }
31174
31511
  const dayKey = getExcursionDayKey(excursionSearchParams.date);
31175
31512
  dispatch(confirmExcursionForDay({
31176
31513
  dayKey,
@@ -31412,6 +31749,12 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
31412
31749
  dispatch(removeEditableEntryLines(added.map((line) => line.guid)));
31413
31750
  return;
31414
31751
  }
31752
+ if (excursionNeedsChoice(result)) {
31753
+ dispatch(setExcursionSearchParams(buildExcursionSearchParams(result)));
31754
+ dispatch(setSelectedExcursionSearchResult(result));
31755
+ dispatch(setFlyInType('excursion-details'));
31756
+ return;
31757
+ }
31415
31758
  const lines = buildTrajectExcursionLines(result);
31416
31759
  if (lines.length)
31417
31760
  dispatch(addEditableEntryLines(lines));
@@ -31426,7 +31769,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
31426
31769
  dispatch(setFlyInIsOpen(false));
31427
31770
  };
31428
31771
  if (isLoading || suggestions?.status === 'loading') {
31429
- return React__default.createElement(React__default.Fragment, null, context.customSpinner ?? React__default.createElement(Spinner, { label: translations.SRP.LOADING_EXCURSIONS }));
31772
+ return React__default.createElement(React__default.Fragment, null, context.customSpinner ?? React__default.createElement(Spinner, { label: translations.SRP.LOADING_ACTIVITIES }));
31430
31773
  }
31431
31774
  return (React__default.createElement("div", { className: "flyin__content" },
31432
31775
  React__default.createElement("div", { className: "search__result-row" },
@@ -32980,6 +33323,27 @@ const getRequestRoomsFromPackagingSegments = (entry, segments) => {
32980
33323
  }));
32981
33324
  };
32982
33325
 
33326
+ /**
33327
+ * The entry to send to the backend, as opposed to the one the page works with.
33328
+ *
33329
+ * They differ only for a traject: the page keeps the line guids the traject was rendered with,
33330
+ * because that is what joins its nodes to its lines, while pricing and booking need the guids the
33331
+ * searches cached their lines under. Anything else passes through untouched, so this can be used
33332
+ * wherever the entry leaves the component without checking which flow is running.
33333
+ */
33334
+ const useBookableEntry = () => {
33335
+ const context = useContext(SearchResultsConfigurationContext);
33336
+ const { editablePackagingEntry, trajectNodeAvailability, trajectFlightAvailability } = useSelector((state) => state.searchResults);
33337
+ const trajectEntry = context?.trajectEntry;
33338
+ return useMemo(() => {
33339
+ if (!editablePackagingEntry)
33340
+ return null;
33341
+ if (!trajectEntry)
33342
+ return editablePackagingEntry;
33343
+ return withCachedEntryLineGuids(editablePackagingEntry, trajectEntry, trajectNodeAvailability, trajectFlightAvailability);
33344
+ }, [editablePackagingEntry, trajectEntry, trajectNodeAvailability, trajectFlightAvailability]);
33345
+ };
33346
+
32983
33347
  const escapeHtml = (value) => {
32984
33348
  if (!value)
32985
33349
  return '';
@@ -33038,7 +33402,57 @@ const findAccommodationForDay = (nodes, currentDay) => {
33038
33402
  }
33039
33403
  return null;
33040
33404
  };
33041
- const buildItineraryHtml = (itinerary, translations) => {
33405
+ const formatTime = (time) => (time ? time.slice(0, 5) : '');
33406
+ /**
33407
+ * Whether generated content actually shows something.
33408
+ *
33409
+ * A content view whose data is missing still returns its wrapper markup, or the whitespace left by
33410
+ * a guard around its body — markup that renders as a blank gap. Only text or an image counts as
33411
+ * something the traveller can read.
33412
+ */
33413
+ const hasVisibleContent = (contents) => {
33414
+ if (!contents)
33415
+ return false;
33416
+ if (/<(img|svg|picture|video|iframe)\b/i.test(contents))
33417
+ return true;
33418
+ return (contents
33419
+ .replace(/<[^>]*>/g, ' ')
33420
+ .replace(/&nbsp;/gi, ' ')
33421
+ .trim().length > 0);
33422
+ };
33423
+ /** The legs of a flight, as the entry line records them. */
33424
+ const buildFlightHtml = (line) => {
33425
+ const flightLines = line.flightInformation?.flightLines ?? [];
33426
+ if (!flightLines.length)
33427
+ return '';
33428
+ const legs = flightLines
33429
+ .map((leg) => {
33430
+ const from = escapeHtml(leg.departureAirportDescription || leg.departureAirportCode);
33431
+ const to = escapeHtml(leg.arrivalAirportDescription || leg.arrivalAirportCode);
33432
+ const carrier = escapeHtml([leg.airlineDescription, leg.flightNumber].filter(Boolean).join(' '));
33433
+ return `<li>${from} ${formatTime(leg.departureTime)} &rarr; ${to} ${formatTime(leg.arrivalTime)}${carrier ? ` &middot; ${carrier}` : ''}</li>`;
33434
+ })
33435
+ .join('');
33436
+ return `<ul class="itinerary-item__legs">${legs}</ul>`;
33437
+ };
33438
+ /**
33439
+ * What to show for an item the generator returned no content for.
33440
+ *
33441
+ * An item comes back with a title and a template but an empty body whenever its template has no
33442
+ * content view of its own — flights, today. Rendering that empty body leaves a blank where a
33443
+ * service should be, so what the entry already knows about the line is drawn instead.
33444
+ */
33445
+ const buildFallbackHtml = (item, line) => {
33446
+ const details = [line?.accommodationName, line?.regimeName].filter(Boolean).join(' — ');
33447
+ return `
33448
+ <div class="itinerary-item__fallback">
33449
+ <h3 class="itinerary-item__fallback-title">${escapeHtml(item.title)}</h3>
33450
+ ${line ? buildFlightHtml(line) : ''}
33451
+ ${details ? `<p class="itinerary-item__fallback-details">${escapeHtml(details)}</p>` : ''}
33452
+ </div>
33453
+ `;
33454
+ };
33455
+ const buildItineraryHtml = (itinerary, translations, lines) => {
33042
33456
  if (!itinerary) {
33043
33457
  return `
33044
33458
  <div class="itinerary-shell">
@@ -33067,7 +33481,9 @@ const buildItineraryHtml = (itinerary, translations) => {
33067
33481
  ? uniqueItems
33068
33482
  .map((item) => `
33069
33483
  <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
33070
- ${item.contents ?? ''}
33484
+ ${hasVisibleContent(item.contents)
33485
+ ? item.contents
33486
+ : buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
33071
33487
  </article>
33072
33488
  `)
33073
33489
  .join('')
@@ -33101,7 +33517,9 @@ const buildItineraryHtml = (itinerary, translations) => {
33101
33517
  ${getUniqueItems(itinerary.defaultItems)
33102
33518
  .map((item) => `
33103
33519
  <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
33104
- ${item.contents ?? ''}
33520
+ ${hasVisibleContent(item.contents)
33521
+ ? item.contents
33522
+ : buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
33105
33523
  </article>
33106
33524
  `)
33107
33525
  .join('')}
@@ -33122,10 +33540,12 @@ const buildItineraryHtml = (itinerary, translations) => {
33122
33540
  const FullItinerary = ({ isLoading }) => {
33123
33541
  const { itinerary } = useSelector((state) => state.searchResults);
33124
33542
  const context = useContext(SearchResultsConfigurationContext);
33543
+ // The entry as it was sent: its line guids are the ones the itinerary items carry back.
33544
+ const bookableEntry = useBookableEntry();
33125
33545
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
33126
33546
  const hostRef = useRef(null);
33127
33547
  const shadowRootRef = useRef(null);
33128
- const html = useMemo(() => buildItineraryHtml(itinerary, translations), [itinerary, translations]);
33548
+ const html = useMemo(() => buildItineraryHtml(itinerary, translations, bookableEntry?.lines ?? []), [itinerary, translations, bookableEntry]);
33129
33549
  useEffect(() => {
33130
33550
  if (isLoading) {
33131
33551
  return;
@@ -33239,6 +33659,35 @@ const FullItinerary = ({ isLoading }) => {
33239
33659
  background: #fff;
33240
33660
  }
33241
33661
 
33662
+ /* An item whose template has no content view of its own still has to read as a service. */
33663
+ .itinerary-item__fallback {
33664
+ padding: 16px 20px;
33665
+ border: 1px solid #e5e7eb;
33666
+ border-radius: 12px;
33667
+ }
33668
+
33669
+ .itinerary-item__fallback-title {
33670
+ margin: 0;
33671
+ font-size: 16px;
33672
+ font-weight: 600;
33673
+ color: #111827;
33674
+ }
33675
+
33676
+ .itinerary-item__legs {
33677
+ margin: 8px 0 0;
33678
+ padding-left: 18px;
33679
+ color: #374151;
33680
+ }
33681
+
33682
+ .itinerary-item__legs li + li {
33683
+ margin-top: 4px;
33684
+ }
33685
+
33686
+ .itinerary-item__fallback-details {
33687
+ margin: 8px 0 0;
33688
+ color: #6b7280;
33689
+ }
33690
+
33242
33691
  .itinerary-node__empty,
33243
33692
  .itinerary-empty {
33244
33693
  padding: 20px;
@@ -33758,6 +34207,8 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
33758
34207
  setSelectedPackagingAccoResult(selectedPackagingAccoResult);
33759
34208
  }
33760
34209
  }, [selectedPackagingAccoResultCode, packagingAccoResults]);
34210
+ // What the page holds versus what the backend can look up; they differ for a traject.
34211
+ const bookableEntry = useBookableEntry();
33761
34212
  if (!context || !editablePackagingEntry || !priceDetails || !config)
33762
34213
  return React__default.createElement(Spinner, { label: translations.SUMMARY.PROCESS_BOOKING });
33763
34214
  const handleSummarySubmit = async (e) => {
@@ -33766,20 +34217,15 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
33766
34217
  if (typeof window !== 'undefined') {
33767
34218
  window.scrollTo(0, 0);
33768
34219
  }
33769
- let updatedEditablePackagingEntry = {
33770
- ...editablePackagingEntry,
33771
- remarks
33772
- };
34220
+ // Held apart from the entry itself so they can be put on both copies of it.
34221
+ let bookingFields = { remarks };
33773
34222
  if (context.generatePaymentUrl && typeof window !== 'undefined') {
33774
34223
  const redirectUrl = new URL(window.location.href);
33775
34224
  redirectUrl.searchParams.set('bookingConfirmation', 'true');
33776
34225
  redirectUrl.searchParams.set('link', '');
33777
- updatedEditablePackagingEntry = {
33778
- ...updatedEditablePackagingEntry,
33779
- redirectUrl: redirectUrl.toString(),
33780
- returnPaymentUrl: true
33781
- };
34226
+ bookingFields = { ...bookingFields, redirectUrl: redirectUrl.toString(), returnPaymentUrl: true };
33782
34227
  }
34228
+ const updatedEditablePackagingEntry = { ...editablePackagingEntry, ...bookingFields };
33783
34229
  dispatch(setEditablePackagingEntry(updatedEditablePackagingEntry));
33784
34230
  try {
33785
34231
  const request = {
@@ -33787,7 +34233,8 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
33787
34233
  officeId: context.tideConnection.officeId,
33788
34234
  catalogueId: context.searchConfiguration.defaultCatalogueId ?? 0,
33789
34235
  agentId: context.agentId,
33790
- payload: updatedEditablePackagingEntry
34236
+ // The page keeps the entry it works with; the backend gets the one it can look up.
34237
+ payload: { ...(bookableEntry ?? editablePackagingEntry), ...bookingFields }
33791
34238
  };
33792
34239
  const bookingResponse = await build.bookPackagingEntry(config, request);
33793
34240
  dispatch(setBookingNumber(bookingResponse.number));
@@ -33819,6 +34266,41 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
33819
34266
  React__default.createElement(WLSidebar, { activeSearchSeed: activeSearchSeed, packagingAccoResult: selectedPackagingAccoResult }))));
33820
34267
  };
33821
34268
 
34269
+ const useTrajectDayActivities = () => {
34270
+ const context = useContext(SearchResultsConfigurationContext);
34271
+ const dispatch = useDispatch();
34272
+ const { trajectDaySuggestions, trajectNodeAvailability } = useSelector((state) => state.searchResults);
34273
+ const trajectEntry = context?.trajectEntry;
34274
+ const showApiItems = !!context?.showApiItems;
34275
+ const loadDay = useCallback(async (day) => {
34276
+ if (!context || !trajectEntry)
34277
+ return;
34278
+ // Already asked for, or already answered: leave it be.
34279
+ if (trajectDaySuggestions[day.order])
34280
+ return;
34281
+ const nodesWithLines = getTrajectNodesWithLines(trajectEntry);
34282
+ const destination = getDayDestination(nodesWithLines, trajectNodeAvailability, day.order);
34283
+ if (!destination)
34284
+ return;
34285
+ dispatch(setTrajectDaySuggestionsLoading(day.order));
34286
+ try {
34287
+ const results = (await build.searchPackagingExcursions(buildTrajectClientConfig(context), buildTrajectDayExcursionRequest(day.date, destination, buildTrajectSearchContext(context, trajectEntry)))) ?? [];
34288
+ // With external items switched off a day only offers what Tide holds itself, and whatever
34289
+ // the traject already books that day has a card of its own further up the block.
34290
+ const configured = getConfiguredExcursionCodes(nodesWithLines, day.order);
34291
+ dispatch(setTrajectDaySuggestions({
34292
+ dayOrder: day.order,
34293
+ results: results.filter((x) => showApiItems || !isExternalResult(x)).filter((x) => !configured.has(x.code))
34294
+ }));
34295
+ }
34296
+ catch (error) {
34297
+ console.error('Failed to load traject day activities', day.order, error);
34298
+ dispatch(setTrajectDaySuggestions({ dayOrder: day.order, results: [] }));
34299
+ }
34300
+ }, [context, trajectEntry, showApiItems, trajectDaySuggestions, trajectNodeAvailability, dispatch]);
34301
+ return loadDay;
34302
+ };
34303
+
33822
34304
  const getNodeIcon = (node) => {
33823
34305
  if (isTrajectFlightNode(node))
33824
34306
  return 'ui-flight';
@@ -33834,6 +34316,7 @@ const MAX_DAY_ACTIVITIES = 6;
33834
34316
  const TrajectResults = ({ isLoading }) => {
33835
34317
  const context = useContext(SearchResultsConfigurationContext);
33836
34318
  const dispatch = useDispatch();
34319
+ const loadDay = useTrajectDayActivities();
33837
34320
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
33838
34321
  const locale = getLocale(context?.languageCode ?? 'en-GB');
33839
34322
  const { trajectNodeAvailability, trajectFlightAvailability, trajectDaySuggestions, editablePackagingEntry } = useSelector((state) => state.searchResults);
@@ -33866,6 +34349,23 @@ const TrajectResults = ({ isLoading }) => {
33866
34349
  });
33867
34350
  return { stayByDay: byDay, stayStartByNodeId: byNodeId };
33868
34351
  }, [nodesWithLines]);
34352
+ /** Whether every node of the trip has an answer, one way or the other. */
34353
+ const tripResolved = useMemo(() => nodesWithLines
34354
+ .filter((x) => isSearchableTrajectNode(x.node) || isTrajectFlightNode(x.node))
34355
+ .every((x) => {
34356
+ const status = isTrajectFlightNode(x.node) ? trajectFlightAvailability[x.line.guid]?.status : trajectNodeAvailability[x.line.guid]?.status;
34357
+ return !!status && status !== 'loading';
34358
+ }), [nodesWithLines, trajectNodeAvailability, trajectFlightAvailability]);
34359
+ /**
34360
+ * Each day's activities are one more search, and a traject has as many as it has days. They go
34361
+ * out together, but only once the trip itself has resolved — sharing the connections with the
34362
+ * searches the trip cannot do without would only make those slower.
34363
+ */
34364
+ useEffect(() => {
34365
+ if (!trajectEntry || !tripResolved)
34366
+ return;
34367
+ (trajectEntry.traject.days ?? []).filter((day) => stayByDay.has(day.order)).forEach((day) => loadDay(day));
34368
+ }, [trajectEntry, tripResolved, stayByDay]);
33869
34369
  if (!context || !trajectEntry)
33870
34370
  return null;
33871
34371
  const handleSelect = (item, code) => {
@@ -33950,7 +34450,7 @@ const TrajectResults = ({ isLoading }) => {
33950
34450
  React__default.createElement(Icon, { name: getNodeIcon(node), height: 16 }),
33951
34451
  React__default.createElement("h3", null,
33952
34452
  React__default.createElement("strong", null, headerTitle || node.name))))),
33953
- status === 'loading' && (React__default.createElement(Spinner, { label: node.serviceType === EXCURSION_SERVICE_TYPE ? translations.SRP.LOADING_EXCURSIONS : translations.SRP.LOADING_ACCOMMODATIONS })),
34453
+ status === 'loading' && React__default.createElement(Spinner, { label: translations.SRP.LOADING_ACCOMMODATIONS }),
33954
34454
  status === 'unavailable' && React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
33955
34455
  status === 'resolved' && selected && (React__default.createElement(React__default.Fragment, null,
33956
34456
  availability.preferredUnavailable && node.preferredAccommodationName && (React__default.createElement("div", { className: "search__results__cards__actions" },
@@ -33986,8 +34486,6 @@ const TrajectResults = ({ isLoading }) => {
33986
34486
  React__default.createElement("div", { className: "search__results__label__text" },
33987
34487
  React__default.createElement(Icon, { name: "ui-flight", height: 16 }),
33988
34488
  React__default.createElement("h3", null,
33989
- translations.SRP.SELECT,
33990
- ' ',
33991
34489
  React__default.createElement("strong", null,
33992
34490
  node.departureAirportCode,
33993
34491
  " - ",
@@ -34013,6 +34511,13 @@ const TrajectResults = ({ isLoading }) => {
34013
34511
  dispatch(removeEditableEntryLines(added.map((line) => line.guid)));
34014
34512
  return;
34015
34513
  }
34514
+ if (excursionNeedsChoice(result)) {
34515
+ dispatch(setExcursionSearchParams(buildExcursionSearchParams(result)));
34516
+ dispatch(setSelectedExcursionSearchResult(result));
34517
+ dispatch(setFlyInType('excursion-details'));
34518
+ dispatch(setFlyInIsOpen(true));
34519
+ return;
34520
+ }
34016
34521
  const lines = buildTrajectExcursionLines(result);
34017
34522
  if (lines.length)
34018
34523
  dispatch(addEditableEntryLines(lines));
@@ -34039,32 +34544,30 @@ const TrajectResults = ({ isLoading }) => {
34039
34544
  const items = getDayExcursionItems(day.order);
34040
34545
  const suggestions = trajectDaySuggestions[day.order];
34041
34546
  const cards = buildDayActivityCards(items, trajectNodeAvailability, suggestions?.results ?? []);
34042
- const isResolving = items.some((item) => (trajectNodeAvailability[item.line.guid]?.status ?? 'loading') === 'loading') || suggestions?.status === 'loading';
34043
- // A day with nothing configured and nothing found has nothing to say. A day whose own
34044
- // excursion came back empty does, so the gap is not passed off as a day without activities.
34045
- if (!cards.length && !items.length)
34547
+ const nodesResolving = items.some((item) => (trajectNodeAvailability[item.line.guid]?.status ?? 'loading') === 'loading');
34548
+ const isSearching = suggestions?.status === 'loading';
34549
+ // A day is worth filling only if the party is staying somewhere that night. The day they fly
34550
+ // home is covered by no stay — the last night was the night before — and nobody is looking for
34551
+ // something to do on it.
34552
+ const canSearch = stayByDay.has(day.order) && !!getDayDestination(nodesWithLines, trajectNodeAvailability, day.order);
34553
+ if (!items.length && !canSearch)
34046
34554
  return null;
34047
34555
  const visible = cards.slice(0, MAX_DAY_ACTIVITIES);
34048
34556
  const hidden = cards.length - visible.length;
34049
34557
  return (React__default.createElement(React__default.Fragment, { key: `activities-${day.order}` },
34050
- React__default.createElement("div", { className: "search__results__label search__results__label--secondary" },
34051
- React__default.createElement("div", { className: "search__results__label__date" },
34052
- React__default.createElement("p", { className: "search__results__label__date-date" }, format$2(new Date(day.date), 'd', { locale })),
34053
- React__default.createElement("p", null, format$2(new Date(day.date), 'MMM', { locale }))),
34558
+ React__default.createElement("div", { className: "search__results__label search__results__label--sub" },
34054
34559
  React__default.createElement("div", { className: "search__results__label__text" },
34055
34560
  React__default.createElement(Icon, { name: "ui-excursion", height: 16 }),
34056
34561
  React__default.createElement("h3", null,
34057
34562
  React__default.createElement("strong", null, translations.SRP.ACTIVITIES)))),
34058
- isResolving && !cards.length && React__default.createElement(Spinner, { label: translations.SRP.LOADING_EXCURSIONS }),
34059
- !isResolving && !cards.length && React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
34060
- !!cards.length && (React__default.createElement(React__default.Fragment, null,
34061
- React__default.createElement("div", { className: "search__results__cards search__results__cards--scroll" }, visible.map(renderActivityCard)),
34062
- hidden > 0 && !hideShowMoreResults && (React__default.createElement("div", { className: "search__results__cards__actions" },
34063
- React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreActivities(day.order) },
34064
- translations.SRP.SHOW_MORE,
34065
- " (",
34066
- hidden,
34067
- ")")))))));
34563
+ (nodesResolving || isSearching) && !cards.length && React__default.createElement(Spinner, { label: translations.SRP.LOADING_ACTIVITIES }),
34564
+ !!cards.length && React__default.createElement("div", { className: "search__results__cards search__results__cards--scroll" }, visible.map(renderActivityCard)),
34565
+ !nodesResolving && !isSearching && !cards.length && (!!items.length || !!suggestions) && (React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)),
34566
+ React__default.createElement("div", { className: "search__results__cards__actions" }, hidden > 0 && !hideShowMoreResults && (React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreActivities(day.order) },
34567
+ translations.SRP.SHOW_MORE,
34568
+ " (",
34569
+ hidden,
34570
+ ")")))));
34068
34571
  };
34069
34572
  /**
34070
34573
  * An activity the traject books is swapped for another, the way a hotel is; one the traveller
@@ -34115,6 +34618,7 @@ const useTrajectAvailability = () => {
34115
34618
  return;
34116
34619
  const controller = new AbortController();
34117
34620
  dispatch(resetTrajectAvailability());
34621
+ dispatch(setTrajectResolving(true));
34118
34622
  const config = {
34119
34623
  host: context.tideConnection.host,
34120
34624
  apiKey: context.tideConnection.apiKey
@@ -34141,42 +34645,51 @@ const useTrajectAvailability = () => {
34141
34645
  const lineGuid = item.line.guid;
34142
34646
  const search = item.node.serviceType === EXCURSION_SERVICE_TYPE ? build.searchPackagingExcursions : build.searchPackagingAccommodations;
34143
34647
  dispatch(setTrajectNodeLoading(lineGuid));
34648
+ const destination = getNodeSearchDestination(nodesWithLines, item);
34649
+ const searchFor = async (productCode) => {
34650
+ try {
34651
+ const results = (await search(config, buildTrajectNodeRequest(item, searchContext, productCode, destination), controller.signal)) ?? [];
34652
+ return results.filter((x) => showApiItems || !isExternalResult(x));
34653
+ }
34654
+ catch (error) {
34655
+ if (!controller.signal.aborted)
34656
+ console.error('Traject product unavailable', item.node.name, productCode, error);
34657
+ return [];
34658
+ }
34659
+ };
34144
34660
  try {
34145
34661
  // With external items switched off the node only looks at products held inside Tide.
34146
34662
  const candidates = getNodeCandidates(item.node).filter((x) => showApiItems || !isExternalCandidate(x));
34147
- const attempts = await Promise.all(candidates.map(async (candidate) => {
34148
- try {
34149
- const attempt = await search(config, buildTrajectNodeRequest(item, searchContext, candidate.code), controller.signal);
34150
- return (attempt ?? []).find((result) => matchesCode(result, candidate.code)) ?? null;
34151
- }
34152
- catch (error) {
34153
- if (!controller.signal.aborted)
34154
- console.error('Traject hotel unavailable', candidate.code, error);
34155
- return null;
34156
- }
34663
+ const areaResults = hideShowMoreResults ? [] : await searchFor('');
34664
+ if (controller.signal.aborted)
34665
+ return [];
34666
+ const configuredResults = [];
34667
+ const found = await Promise.all(candidates.map(async (candidate) => {
34668
+ const known = areaResults.find((x) => matchesCode(x, candidate.code));
34669
+ if (known)
34670
+ return known;
34671
+ return (await searchFor(candidate.code)).find((x) => matchesCode(x, candidate.code)) ?? null;
34157
34672
  }));
34158
- const configuredResults = attempts
34159
- .filter((x) => !!x)
34160
- .filter((x, i, all) => all.findIndex((y) => y.code === x.code) === i);
34673
+ if (controller.signal.aborted)
34674
+ return [];
34675
+ // Two configured codes can land on the same product — a hotel and one of its room types.
34676
+ found.forEach((result) => {
34677
+ if (!result || configuredResults.some((x) => x.code === result.code))
34678
+ return;
34679
+ configuredResults.push(result);
34680
+ });
34681
+ const configuredCodes = new Set(configuredResults.map((x) => x.code));
34682
+ const destinationResults = areaResults.filter((x) => !configuredCodes.has(x.code));
34161
34683
  const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
34162
- let preferredUnavailable = !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code));
34163
- // The destination search exists only to stock the show-more fly-in. With the button hidden
34164
- // a node is limited to its configured products; if none are available it simply reports so,
34165
- // rather than quietly substituting a hotel nobody configured.
34166
- const destinationResults = hideShowMoreResults
34167
- ? []
34168
- : ((await search(config, buildTrajectNodeRequest(item, searchContext, ''), controller.signal)) ?? [])
34169
- .filter((x) => showApiItems || !isExternalResult(x))
34170
- .filter((x) => !configuredResults.some((configured) => configured.code === x.code));
34684
+ const preferredUnavailable = configuredResults.length
34685
+ ? !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code))
34686
+ : candidates.length > 0;
34171
34687
  const picked = configuredResults[0] ?? pickCheapest(destinationResults);
34172
- const results = [...configuredResults, ...destinationResults];
34173
- if (!configuredResults.length)
34174
- preferredUnavailable = candidates.length > 0;
34175
34688
  if (controller.signal.aborted)
34176
34689
  return [];
34177
34690
  dispatch(setTrajectNodeAvailability({
34178
34691
  lineGuid,
34179
- results,
34692
+ results: [...configuredResults, ...destinationResults],
34180
34693
  selectedCode: picked?.code ?? null,
34181
34694
  preferredUnavailable
34182
34695
  }));
@@ -34220,11 +34733,11 @@ const useTrajectAvailability = () => {
34220
34733
  }
34221
34734
  };
34222
34735
  (async () => {
34223
- const searchable = nodesWithLines.filter((x) => isSearchableTrajectNode(x.node));
34224
- const flightNodes = nodesWithLines.filter((x) => isTrajectFlightNode(x.node));
34225
- const unresolved = (await Promise.all([...searchable.map(resolveNode), ...flightNodes.map(resolveFlightNode)])).flat();
34736
+ const resolvable = nodesWithLines.filter((x) => isSearchableTrajectNode(x.node) || isTrajectFlightNode(x.node));
34737
+ const unresolved = (await Promise.all(resolvable.map((item) => (isTrajectFlightNode(item.node) ? resolveFlightNode(item) : resolveNode(item))))).flat();
34226
34738
  if (controller.signal.aborted)
34227
34739
  return;
34740
+ dispatch(setTrajectResolving(false));
34228
34741
  // A node with no availability anywhere cannot be priced or booked, so its line comes off
34229
34742
  // the entry. The card stays visible and reports the gap.
34230
34743
  const emptyLineGuids = unresolved.filter(Boolean);
@@ -34240,10 +34753,12 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
34240
34753
  const dispatch = useDispatch();
34241
34754
  const context = useContext(SearchResultsConfigurationContext);
34242
34755
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
34243
- const { results, filteredResults, packagingAccoResults, filteredPackagingAccoResults, isLoading, flightsLoading, initialFilters, filters, flightFilters, selectedSortType, selectedFlightSortType, selectedSearchResult, selectedPackagingAccoResultCode, flyInIsOpen, packagingAccoSearchDetails, editablePackagingEntry, transactionId, flyInType, packagingFlightResults, confirmedExcursionsByDay, bookPackagingEntry, trajectEditLineGuid } = useSelector((state) => state.searchResults);
34756
+ 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 } = useSelector((state) => state.searchResults);
34244
34757
  const isMobile = useMediaQuery('(max-width: 1200px)');
34245
34758
  // Resolves live availability per traject node; no-op unless a traject entry is configured.
34246
34759
  useTrajectAvailability();
34760
+ // The entry as the backend can price it: a traject swaps in the guids its searches cached.
34761
+ const bookableEntry = useBookableEntry();
34247
34762
  const [initialFiltersSet, setInitialFiltersSet] = useState(false);
34248
34763
  const [initialFlightFiltersSet, setInitialFlightFiltersSet] = useState(false);
34249
34764
  const [filtersOpen, setFiltersOpen] = useState(false);
@@ -35010,6 +35525,10 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35010
35525
  const fetchPriceDetails = async () => {
35011
35526
  if (!context || !editablePackagingEntry || isEmpty$1(editablePackagingEntry.lines))
35012
35527
  return;
35528
+ // A traject resolves one node at a time. Pricing each step would show a total that climbs
35529
+ // as the trip fills in, so the whole trip is priced once, when the last node is in.
35530
+ if (context.trajectEntry && trajectResolving)
35531
+ return;
35013
35532
  setPricesAreLoading(true);
35014
35533
  try {
35015
35534
  const config = {
@@ -35021,7 +35540,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35021
35540
  officeId: context.tideConnection.officeId,
35022
35541
  catalogueId: context.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
35023
35542
  agentId: context.agentId,
35024
- payload: editablePackagingEntry
35543
+ payload: bookableEntry ?? editablePackagingEntry
35025
35544
  };
35026
35545
  const priceDetails = await build.getPriceDetails(config, request);
35027
35546
  dispatch(setPriceDetails(priceDetails));
@@ -35032,11 +35551,15 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35032
35551
  setPricesAreLoading(false);
35033
35552
  }
35034
35553
  };
35554
+ fetchPriceDetails();
35555
+ }, [editablePackagingEntry, bookableEntry, trajectResolving]);
35556
+ useEffect(() => {
35035
35557
  const fetchItinerary = async () => {
35036
35558
  if (!context || !context.packagingEntry || !editablePackagingEntry || isEmpty$1(editablePackagingEntry.lines))
35037
35559
  return;
35038
- // The traject view renders its own day-by-day column, so the generated itinerary is unused.
35039
- if (context.trajectEntry)
35560
+ // While a traject is being composed its own day-by-day column is the itinerary; the generated
35561
+ // one is what the traveller reads the finished trip back from, so it waits until then.
35562
+ if (context.trajectEntry && (trajectResolving || !trajectItineraryView))
35040
35563
  return;
35041
35564
  setItineraryIsLoading(true);
35042
35565
  try {
@@ -35049,7 +35572,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35049
35572
  officeId: context.tideConnection.officeId,
35050
35573
  catalogueId: context.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
35051
35574
  agentId: context.agentId,
35052
- payload: editablePackagingEntry
35575
+ payload: bookableEntry ?? editablePackagingEntry
35053
35576
  };
35054
35577
  const itinerary = await build.getItinerary(config, request);
35055
35578
  dispatch(setItinerary(itinerary));
@@ -35060,9 +35583,8 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35060
35583
  setItineraryIsLoading(false);
35061
35584
  }
35062
35585
  };
35063
- fetchPriceDetails();
35064
35586
  fetchItinerary();
35065
- }, [editablePackagingEntry]);
35587
+ }, [editablePackagingEntry, bookableEntry, trajectResolving, trajectItineraryView]);
35066
35588
  // Flight selection
35067
35589
  // const [selectedOutwardKey, setSelectedOutwardKey] = useState<string | null>(null);
35068
35590
  // const [selectedReturnKey, setSelectedReturnKey] = useState<string | null>(null);
@@ -35426,7 +35948,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35426
35948
  context.searchConfiguration.qsmType != build.PortalQsmType.AccommodationAndFlight && context.showFilters && (React__default.createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => setFiltersOpen(!filtersOpen),
35427
35949
  // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
35428
35950
  isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) })),
35429
- context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default.createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading, onEditAccommodation: handleEditAccommodation })),
35951
+ context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default.createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading || trajectResolving, onEditAccommodation: handleEditAccommodation })),
35430
35952
  React__default.createElement("div", { className: "search__results" },
35431
35953
  isMobile && (React__default.createElement("div", { className: "search__result-row" },
35432
35954
  React__default.createElement("div", { className: "search__results__actions" },
@@ -35500,7 +36022,8 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35500
36022
  React__default.createElement("div", { className: "search__results__cards search__results__cards--extended" },
35501
36023
  selectedReturnKey && selectedReturn && (React__default.createElement(IndependentFlightOption, { key: `flight-${selectedReturnKey}`, item: selectedReturn.return, guid: selectedReturn.outwardGuid, selectedGuid: selectedReturn.outwardGuid, isOutward: false, showSelectedState: true, price: selectedReturn.price })),
35502
36024
  uniqueReturnFlights.map((result) => (React__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 })))))),
35503
- context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.trajectEntry && (React__default.createElement(TrajectResults, { isLoading: isLoading })),
36025
+ context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.trajectEntry && !trajectItineraryView && (React__default.createElement(TrajectResults, { isLoading: isLoading })),
36026
+ context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.trajectEntry && trajectItineraryView && (React__default.createElement(FullItinerary, { isLoading: itineraryIsLoading })),
35504
36027
  context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && context.packagingEntry && !context.trajectEntry && (React__default.createElement(FullItinerary, { isLoading: itineraryIsLoading })))),
35505
36028
  React__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 })))))))));
35506
36029
  };