@sabstravtech/obtservices 0.2.2605260950 → 0.2.2606091200
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/angular/components/basket-panel.d.ts +1 -1
- package/angular/fetchers.d.ts +2 -0
- package/angular/lib/vendor/classes/helpers.d.ts +2 -2
- package/angular/lib/vendor/fetchers/download-eticket.fetcher.d.ts +10 -0
- package/angular/lib/vendor/fetchers/get-flight-exchange-details.fetcher.d.ts +1 -1
- package/angular/lib/vendor/fetchers/get-flight-search-exchange.fetcher.d.ts +1 -1
- package/angular/lib/vendor/fetchers/get-jyrney-url.fetcher.d.ts +10 -0
- package/angular/lib/vendor/fetchers/get-latest-versions.fetcher.d.ts +1 -1
- package/angular/lib/vendor/fetchers/get-process-terms-price.fetcher.d.ts +10 -0
- package/angular/lib/vendor/fetchers/get-rail-search-exchange.fetcher.d.ts +1 -1
- package/angular/lib/vendor/interceptors/http-cancel-interceptor.d.ts +3 -3
- package/angular/lib/vendor/services/chatbot-eurostar-search.service.d.ts +38 -0
- package/angular/lib/vendor/services/chatbot-flight-search.service.d.ts +100 -0
- package/angular/lib/vendor/services/chatbot-hotel-search.service.d.ts +32 -0
- package/angular/lib/vendor/services/chatbot-rail-search.service.d.ts +53 -0
- package/angular/lib/vendor/services/chatbot-search-dispatcher.service.d.ts +36 -0
- package/angular/lib/vendor/services/chatbot.service.d.ts +34 -2
- package/angular/lib/vendor/services/enterprise-basket.service.d.ts +15 -12
- package/angular/lib/vendor/services/hotel-avallibility.service.d.ts +2 -2
- package/angular/lib/vendor/services/http-call.service.d.ts +3 -3
- package/angular/lib/vendor/services/http-cancel.service.d.ts +1 -1
- package/angular/lib/vendor/services/logon.service.d.ts +5 -5
- package/angular/lib/vendor/services/messages.service.d.ts +1 -1
- package/angular/lib/vendor/services/my-bookings.service.d.ts +2 -2
- package/angular/lib/vendor/services/prompt-update.service.d.ts +1 -1
- package/angular/lib/vendor/services/result-aware.service.d.ts +30 -1
- package/angular/lib/vendor/services/search-document-validation.service.d.ts +1 -1
- package/angular/lib/vendor/services/search-payment-validation.service.d.ts +1 -1
- package/angular/lib/vendor/services/search.service.d.ts +16 -15
- package/angular/lib/vendor/services/user.service.d.ts +7 -7
- package/angular/lib/vendor/services/webtoken.service.d.ts +1 -1
- package/angular/lib/vendor/testers/postcode-validate.tester.d.ts +1 -1
- package/angular/lib/vendor/types/graphql.angular.types.d.ts +132 -0
- package/angular/lib/vendor/updaters/accept-new-price.updater.d.ts +1 -1
- package/angular/lib/vendor/updaters/accept-process-terms.updater.d.ts +9 -0
- package/angular/lib/vendor/updaters/add-basket-item-markup.updater.d.ts +1 -1
- package/angular/lib/vendor/updaters/amend-booking.updater.d.ts +1 -1
- package/angular/lib/vendor/updaters/edit-user-address.updater.d.ts +1 -1
- package/angular/lib/vendor/updaters/save-user-address.updater.d.ts +1 -1
- package/angular/lib/vendor/updaters/set-empty-user-mi-default-value.updater.d.ts +1 -1
- package/angular/lib/vendor/updaters/set-mi-values.updater.d.ts +1 -1
- package/angular/lib/vendor/updaters/set-user-mi-default-value.updater.d.ts +1 -1
- package/angular/services.d.ts +5 -0
- package/base/lib/constants/ticket-codes.d.ts +1 -1
- package/base/lib/vendor/classes/base-enterprise.d.ts +1 -1
- package/base/lib/vendor/classes/base-network-call.d.ts +1 -1
- package/base/lib/vendor/classes/basket-info-mi-details.impl.d.ts +1 -1
- package/base/lib/vendor/classes/cabhire-enterprise-search.d.ts +3 -3
- package/base/lib/vendor/classes/carhire-enterprise-search.d.ts +3 -3
- package/base/lib/vendor/classes/draft-basket-info-mi-details.impl.d.ts +1 -1
- package/base/lib/vendor/classes/draft-management-info-and-valid.d.ts +2 -2
- package/base/lib/vendor/classes/eurostar-enterprise-search.d.ts +5 -5
- package/base/lib/vendor/classes/ferry-enterprise-search.d.ts +2 -4
- package/base/lib/vendor/classes/flight-enterprise-search.d.ts +5 -5
- package/base/lib/vendor/classes/hotel-enterprise-search.d.ts +4 -4
- package/base/lib/vendor/classes/irl-enterprise-search.d.ts +2 -2
- package/base/lib/vendor/classes/lounges-enterprise-search.d.ts +4 -4
- package/base/lib/vendor/classes/management-info-and-valid.d.ts +1 -1
- package/base/lib/vendor/classes/modal-types.enum.d.ts +1 -0
- package/base/lib/vendor/classes/parking-enterprise-search.d.ts +2 -2
- package/base/lib/vendor/classes/rail-enterprise-search.d.ts +5 -7
- package/base/lib/vendor/classes/season-ticket-enterprise-search.d.ts +2 -2
- package/base/lib/vendor/classes/supplementary-booking-info-impl.d.ts +1 -1
- package/base/lib/vendor/fetchers/apply-JIT-hotel-rules.fetcher.d.ts +3 -3
- package/base/lib/vendor/fetchers/eurostar-quote.fetcher.d.ts +2 -2
- package/base/lib/vendor/fetchers/flight-quote.fetcher.d.ts +3 -3
- package/base/lib/vendor/fetchers/hotel-avaliability-fetcher.d.ts +2 -2
- package/base/lib/vendor/fetchers/object-fetchable-enterprise.d.ts +1 -1
- package/base/lib/vendor/interceptors/http-cancel-interceptor.d.ts +2 -2
- package/base/lib/vendor/interfaces/Iambulance-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Iapartment-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Icarhire-enterprise-search.d.ts +2 -2
- package/base/lib/vendor/interfaces/Ienterprise-basket.service.d.ts +6 -5
- package/base/lib/vendor/interfaces/Ieurostar-enterprise-search.d.ts +2 -2
- package/base/lib/vendor/interfaces/Ifast-track-enterprise-search.d.ts +2 -2
- package/base/lib/vendor/interfaces/Iferry-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Iflight-enterprise-search.d.ts +4 -4
- package/base/lib/vendor/interfaces/Ihotel-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Iirl-enterprise-search.d.ts +2 -2
- package/base/lib/vendor/interfaces/Ilounges-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Imeeting-room-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Imybookings.service.d.ts +1 -2
- package/base/lib/vendor/interfaces/Irail-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Irail-station-information.interface.d.ts +1 -1
- package/base/lib/vendor/interfaces/Isearch.service.d.ts +4 -5
- package/base/lib/vendor/interfaces/Iseason-tickets-enterprise-search.d.ts +1 -1
- package/base/lib/vendor/interfaces/Iuser.service.d.ts +4 -4
- package/base/lib/vendor/interfaces/booking-baskes-duplicate-check.interface.d.ts +1 -1
- package/base/lib/vendor/interfaces/cab-hire-search-arg.interface.d.ts +2 -2
- package/base/lib/vendor/interfaces/car-hire-classes.interface.d.ts +1 -1
- package/base/lib/vendor/interfaces/car-hire-search-arg.interface.d.ts +1 -1
- package/base/lib/vendor/interfaces/chatbot.types.d.ts +325 -3
- package/base/lib/vendor/interfaces/cheapest-price-object.d.ts +1 -1
- package/base/lib/vendor/interfaces/eurostar-search-arg.interface.d.ts +2 -2
- package/base/lib/vendor/interfaces/flight-multi-destination.interface.d.ts +3 -3
- package/base/lib/vendor/interfaces/hotel-recent-search-args.interface.d.ts +2 -2
- package/base/lib/vendor/interfaces/hotel-results-configuration.interface.d.ts +1 -1
- package/base/lib/vendor/interfaces/irl-search-arg.interface.d.ts +1 -1
- package/base/lib/vendor/operations/simple-fetchable-list-approval.d.ts +1 -1
- package/base/lib/vendor/operations/simple-fetchable-list-basket.d.ts +14 -1
- package/base/lib/vendor/operations/simple-fetchable-list-flight.d.ts +1 -1
- package/base/lib/vendor/operations/simple-fetchable-list-location.d.ts +1 -1
- package/base/lib/vendor/operations/simple-fetchable-list-misc.d.ts +14 -1
- package/base/lib/vendor/operations/simple-fetchable-list-quotes.d.ts +1 -1
- package/base/lib/vendor/operations/simple-fetchable-list-search.d.ts +1 -1
- package/base/lib/vendor/operations/simple-fetchable-list-user.d.ts +1 -1
- package/base/lib/vendor/operations/simple-fetchable-object.d.ts +14 -1
- package/base/lib/vendor/operations/simple-fetchers.d.ts +1 -0
- package/base/lib/vendor/operations/simple-updaters-basket-approval.d.ts +1 -1
- package/base/lib/vendor/operations/simple-updaters-basket-item.d.ts +14 -1
- package/base/lib/vendor/operations/simple-updaters-basket.d.ts +1 -1
- package/base/lib/vendor/operations/simple-updaters-booking.d.ts +1 -1
- package/base/lib/vendor/operations/simple-updaters-user-preference.d.ts +1 -1
- package/base/lib/vendor/operations/simple-updaters-user.d.ts +1 -1
- package/base/lib/vendor/services/chatbot.service.d.ts +43 -1
- package/base/lib/vendor/services/enterprise-basket.service.d.ts +17 -18
- package/base/lib/vendor/services/http-cancel.service.d.ts +1 -1
- package/base/lib/vendor/services/logon.service.d.ts +3 -3
- package/base/lib/vendor/services/my-bookings.service.d.ts +3 -6
- package/base/lib/vendor/services/requires-override-reason.service.d.ts +4 -4
- package/base/lib/vendor/services/requires-reason.service.d.ts +2 -2
- package/base/lib/vendor/services/search.service.d.ts +10 -9
- package/base/lib/vendor/services/user.service.d.ts +2 -6
- package/base/lib/vendor/testers/base.tester.d.ts +1 -1
- package/base/lib/vendor/testers/postcode-validate.tester.d.ts +2 -2
- package/base/lib/vendor/types/basket-types.d.ts +2 -2
- package/base/lib/vendor/types/graphql.types.d.ts +104 -0
- package/base/lib/vendor/types/types.d.ts +37 -36
- package/fesm2022/sabstravtech-obtservices-angular.mjs +2746 -155
- package/fesm2022/sabstravtech-obtservices-angular.mjs.map +1 -1
- package/fesm2022/sabstravtech-obtservices-base.mjs +6431 -6048
- package/fesm2022/sabstravtech-obtservices-base.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, Component, Inject, NgModule, Pipe } from '@angular/core';
|
|
3
|
-
import { BaseEnvironment, BaseEventMessenager, BaseHelperRoutines, PaymentMethodType, SchemaFormat, ServiceProvider, BaseModalOpenerService, BaseAcceptNewPriceUpdater, BaseAddItemToBasketUpdater, BaseAddUserToBasketItemUpdater, BaseCreateBasketUpdater, BaseGetBasketFetcher, BaseBookBasketUpdater, BaseDeleteBasketUpdater, BaseRemoveItemFromBasketUpdater, BaseRevalidateBasketUpdater, BaseGetUserFetcher, BaseGetUserBasketsFetcher, BaseSetBasketItemLeadPassengerUpdater, BaseMoveBasketItemToBasket, BaseUpdateBasketNoteUpdater, BaseUpdateBasketTitleUpdater, BaseUserUiConfigsFetcher, BaseUserProductsFetcher, BaseUserMessagesFetcher, BaseGetCompaniesFetcher, BaseGetCompanyFetcher, BaseConfirmMessagesUpdater, BaseSetUserLanguageUpdater, BaseEditUserUpdater, BaseCreateGuestUpdater, BaseGetUserGuestsFetcher, BaseGetOfficeFetcher, BaseRedirectApproverFetcher, BaseAddRedirectApproverUpdater, BaseRemoveRedirectApproverUpdater, BaseStorageService, BaseWebTokenService, BaseSearchCompanyApproversFetcher, BaseAddFavouriteUserUpdater, BaseGetUserFavouriteUsersFetcher, BaseRemoveFavouriteUserUpdater, BaseApexxCreateCardUpdater, BaseApexxDeleteCardUpdater, BaseApexxUpdateCardUpdater, BaseGetApexxListCardsFetcher, BaseSetBasketItemApexxTokenUpdater, BaseGetUserMiFetcher, BaseGetUserDocumentLoyaltyFetcher, BaseGetUserEmergencyContactFetcher, BaseDeleteGuestUpdater, BaseSearchUserWithEmailFetcher, BaseGetOfficeUsersFetcher, BaseRemovePreferredTransportHubUpdater, BaseSetPreferredTransportHubUpdater, BaseGetUserPreferredTransportHubsFetcher, BaseGetUserMiDefaultValuesFetcher, BaseGetUserMiStackFetcher, BaseSetUserMIDefaultValueUpdater, BaseGetEmptyUserMiDefaultValuesFetcher, BaseSetEmptyUserMIDefaultValueUpdater, BaseSearchCompanyUsersFetcher, BaseTrainlineTenantIsFrenchFetcher, BaseUserService, BaseSetPaymentOptionUpdater, BaseGetCancellationInfoFetcher, BaseAddGuestToBasketItemUpdater, BaseAddUpdatedItemToBasketUpdater, BaseCreateBasketNoteUpdater, BaseGetBasketNotesFetcher, BaseGetBasketApprovalTimestampsFetcher, BaseAddBasketItemMarkupUpdater, BaseAddBasketItemCustomRemarkUpdater, BaseGetBasketItemCustomRemarksFetcher, BaseChangeBasketOwnershipUpdater, BaseGetBasketQuoteFetcher, BaseResendApproverEmailFetcher, BaseCheckIfBasketRequiresApprovalFetcher, BaseGetOfficeApproversFetcher, BaseSelectBasketApproverUpdater, BaseGetDraftBasketsFetcher, BaseCreateBasketFromDraftUpdater, BaseSelectBasketNotifyApproverUpdater, BaseCanAmendBookingFetcher, BaseGetUserApproversFetcher, BaseGetMIApproversFetcher, BaseGetBasketApprovalInfoFetcher, BaseShareBasketUpdater, BaseGetUserSharedBasketsFetcher, BaseGetTrainSeatMapFetcher, BaseBeforeAmendCabSearchFetcher, BaseBeforeAmendCarHireSearchFetcher, BaseSendBackToQueueUpdater, BaseGetBasketCo2InfoFetcher, BaseSelectBasketMultiLevelApproversUpdater, BaseValidateBasketMiFetcher, BaseValidateBasketItemMiFetcher, BaseSetMIValuesUpdater, BaseSearchMIAutoSuggestValuesFetcher, BaseGetTrainSeatmapEvolviFetcher, BaseReserveRailSeatsUpdater, BaseGetEvolviSeatmapsAreEnabledFetcher, BasegetRequestedBookingUpdateFetcher, BaseUpdateExchangeBasketUpdater, BaseEnterpriseBasketService, BaseHotelAvalibilityQuoteFetcher, BaseHotelAvalibilityService, AbstractHttpCallService, BaseSearchAirportsFetcher, BaseSearchAirlinesFetcher, BaseSearchCityFetcher, BaseSearchPostcodeFetcher, BaseSearchRailStationsFetcher, BaseSaveUserAddressUpdater, BaseEditUserAddressUpdater, BaseUserAddressesFetcher, BaseGetUserCompanyOfficesFetcher, BaseSearchHotelChainsFetcher, BaseGetConfermaRoomImagesFetcher, BaseValidateIsPostcodeValidTester, BaseDeleteUserAddressUpdater, BaseDeleteRecentSearchUpdater, BaseSaveRecentSearchUpdater, BaseGetUserCarbonAllowanceFetcher, BaseSaveFavouriteSearchUpdater, BaseGetHotelChainsFetcher, BaseGetRailProvidersFetcher, BaseGetAllAirlinesFetcher, BaseGetCarHireProvidersFetcher, BaseLoungeQuoteFetcher, BaseParkingQuoteFetcher, BaseFlightQuoteFetcher, BaseHotelQuoteFetcher, BaseCarhireQuoteFetcher, BaseCarHireAvailabilityDetailFetcher, BaseCabhireQuoteFetcher, BaseRailQuoteFetcher, BaseIrlQuoteFetcher, BaseSearchDepotFetcher, BaseGetRailStationInfoFetcher, BaseGetRailStationFetcher, BaseGetIrlSupplierStationFetcher, BaseGetRiskAlertsFetcher, BaseSearchConfermaQuicklistFetcher, BaseGetUserRecentSearchesFetcher, BaseGetRailcardsFetcher, BaseGetCovidMicrositeTokenFetcher, BaseEurostarQuoteFetcher, BaseSearchUsersCanBookForFetcher, BaseSearchIrlStationsFetcher, BaseIrlDiscountCardFetcher, BaseGetFlightFareRulesFetcher, BaseGetFlightSeatMapFetcher, BaseGetRailLiveDeparturesFetcher, BaseHotelRulesFetcher, BaseApplyJitFlightRulesFetcher, BaseGetUserFavouriteSearchesFetcher, BaseDeleteFavouriteSearchUpdater, BaseEmailBasketFetcher, BaseFastTrackQuoteFetcher, BaseSendOfflineNotificationFetcher, BaseGenerateBasketPdfFetcher, BaseEmailFerryBookingFetcher, BaseGetFlightExtrasOptionsFetcher, BaseConvertCurrencyFetcher, BaseGetCurrencyConversionRatesFetcher, BaseGetHotelDetailsFetcher, BaseGetServiceFetcher, BaseGetFlightBrandedFaresFetcher, BaseGetTrainlineSearchConfigFetcher, BaseSearchRailInwardFetcher, BaseRailSearchExchangeFetcher, BaseCreateItineraryExchangeUpdater, BaseGetAirAvailabilityFetcher, BaseGetFlightAtNewClassFetcher, BaseGetFlightUpsellOffersFetcher, BaseGetOfficesFetcher, BaseFlightSearchExchangeFetcher, BaseFlightExchangeDetailsFetcher, BaseEmailApartmentBookingFetcher, BaseEmailSeasonTicketBookingFetcher, BaseSuggestPlacesFetcher, BaseGetLatLonFromHereIdFetcher, BaseGetGmtItineraryOptionsFetcher, BaseGetGutCitySuggestionsFetcher, BaseGetGutLocationSuggestionsFetcher, BaseGetRouteHappyFetcher, BaseGetMultipleHotelRatingFetcher, BaseEmailMeetingRoomBookingFetcher, BaseEmailAmbulanceBookingFetcher, BaseGetEntLocationByPostcode, BaseGetEntLocationByCity, BaseGetUserRecentBoltSearchesFetcher, BaseSaveRecentBoltSearchUpdater, BaseGetFerryPortsFetcher, BaseGetBannerFetcher, BaseUpdateDOBUpdater, BaseUpdateFlightSeatMapUpdater, BaseDeleteAllRecentSearchesUpdater, BaseGetOfficeDivisionsFetcher, BaseSearchGeoLocationFetcher, BaseSearchDiscoverLocationFetcher, BaseGetPriceHiddenSabreExchangeFetcher, BaseEnterpriseSearchService, BaseGetLatestVersionsFetcher, BaseLogonService, BaseCancelBookingUpdater, BaseAmendBookingUpdater, BaseCheckForDuplicateBookingsFetcher, BaseEnterpriseMyBookingsService, BaseRouteHappyService, BaseHttpCancelService, BaseMessagesService, RailFareNameKeywords, RailSplitType, isGroupTicket, RailFareTypes, SingleOrReturn, FlightSearchType, FlightDirectionEnum, BaseChatbotService, UserFavorurite, ModalTypes, BaseGetMiRequiringMandatoryDefaultValueFetcher, BaseHttpCancelInterceptor } from '@sabstravtech/obtservices/base';
|
|
3
|
+
import { BaseEnvironment, BaseEventMessenager, BaseHelperRoutines, PaymentMethodType, SchemaFormat, ServiceProvider, BaseModalOpenerService, BaseAcceptNewPriceUpdater, BaseAddItemToBasketUpdater, BaseAddUserToBasketItemUpdater, BaseCreateBasketUpdater, BaseGetBasketFetcher, BaseBookBasketUpdater, BaseDeleteBasketUpdater, BaseRemoveItemFromBasketUpdater, BaseRevalidateBasketUpdater, BaseGetUserFetcher, BaseGetUserBasketsFetcher, BaseSetBasketItemLeadPassengerUpdater, BaseMoveBasketItemToBasket, BaseUpdateBasketNoteUpdater, BaseUpdateBasketTitleUpdater, BaseUserUiConfigsFetcher, BaseUserProductsFetcher, BaseUserMessagesFetcher, BaseGetCompaniesFetcher, BaseGetCompanyFetcher, BaseConfirmMessagesUpdater, BaseSetUserLanguageUpdater, BaseEditUserUpdater, BaseCreateGuestUpdater, BaseGetUserGuestsFetcher, BaseGetOfficeFetcher, BaseRedirectApproverFetcher, BaseAddRedirectApproverUpdater, BaseRemoveRedirectApproverUpdater, BaseStorageService, BaseWebTokenService, BaseSearchCompanyApproversFetcher, BaseAddFavouriteUserUpdater, BaseGetUserFavouriteUsersFetcher, BaseRemoveFavouriteUserUpdater, BaseApexxCreateCardUpdater, BaseApexxDeleteCardUpdater, BaseApexxUpdateCardUpdater, BaseGetApexxListCardsFetcher, BaseSetBasketItemApexxTokenUpdater, BaseGetUserMiFetcher, BaseGetUserDocumentLoyaltyFetcher, BaseGetUserEmergencyContactFetcher, BaseDeleteGuestUpdater, BaseSearchUserWithEmailFetcher, BaseGetOfficeUsersFetcher, BaseRemovePreferredTransportHubUpdater, BaseSetPreferredTransportHubUpdater, BaseGetUserPreferredTransportHubsFetcher, BaseGetUserMiDefaultValuesFetcher, BaseGetUserMiStackFetcher, BaseSetUserMIDefaultValueUpdater, BaseGetEmptyUserMiDefaultValuesFetcher, BaseSetEmptyUserMIDefaultValueUpdater, BaseSearchCompanyUsersFetcher, BaseTrainlineTenantIsFrenchFetcher, BaseUserService, BaseSetPaymentOptionUpdater, BaseGetCancellationInfoFetcher, BaseAddGuestToBasketItemUpdater, BaseAddUpdatedItemToBasketUpdater, BaseCreateBasketNoteUpdater, BaseGetBasketNotesFetcher, BaseGetBasketApprovalTimestampsFetcher, BaseAddBasketItemMarkupUpdater, BaseAddBasketItemCustomRemarkUpdater, BaseGetBasketItemCustomRemarksFetcher, BaseChangeBasketOwnershipUpdater, BaseGetBasketQuoteFetcher, BaseGetProcessTermsPriceFetcher, BaseAcceptProcessTermsUpdater, BaseResendApproverEmailFetcher, BaseCheckIfBasketRequiresApprovalFetcher, BaseGetOfficeApproversFetcher, BaseSelectBasketApproverUpdater, BaseGetDraftBasketsFetcher, BaseCreateBasketFromDraftUpdater, BaseSelectBasketNotifyApproverUpdater, BaseCanAmendBookingFetcher, BaseGetUserApproversFetcher, BaseGetMIApproversFetcher, BaseGetBasketApprovalInfoFetcher, BaseShareBasketUpdater, BaseGetUserSharedBasketsFetcher, BaseGetTrainSeatMapFetcher, BaseBeforeAmendCabSearchFetcher, BaseBeforeAmendCarHireSearchFetcher, BaseSendBackToQueueUpdater, BaseGetBasketCo2InfoFetcher, BaseSelectBasketMultiLevelApproversUpdater, BaseValidateBasketMiFetcher, BaseValidateBasketItemMiFetcher, BaseSetMIValuesUpdater, BaseSearchMIAutoSuggestValuesFetcher, BaseGetTrainSeatmapEvolviFetcher, BaseReserveRailSeatsUpdater, BaseGetEvolviSeatmapsAreEnabledFetcher, BasegetRequestedBookingUpdateFetcher, BaseUpdateExchangeBasketUpdater, BaseDownloadETicketFetcher, BaseEnterpriseBasketService, BaseHotelAvalibilityQuoteFetcher, BaseHotelAvalibilityService, AbstractHttpCallService, BaseSearchAirportsFetcher, BaseSearchAirlinesFetcher, BaseSearchCityFetcher, BaseSearchPostcodeFetcher, BaseSearchRailStationsFetcher, BaseSaveUserAddressUpdater, BaseEditUserAddressUpdater, BaseUserAddressesFetcher, BaseGetUserCompanyOfficesFetcher, BaseSearchHotelChainsFetcher, BaseGetConfermaRoomImagesFetcher, BaseValidateIsPostcodeValidTester, BaseDeleteUserAddressUpdater, BaseDeleteRecentSearchUpdater, BaseSaveRecentSearchUpdater, BaseGetUserCarbonAllowanceFetcher, BaseSaveFavouriteSearchUpdater, BaseGetHotelChainsFetcher, BaseGetRailProvidersFetcher, BaseGetAllAirlinesFetcher, BaseGetCarHireProvidersFetcher, BaseLoungeQuoteFetcher, BaseParkingQuoteFetcher, BaseFlightQuoteFetcher, BaseHotelQuoteFetcher, BaseCarhireQuoteFetcher, BaseCarHireAvailabilityDetailFetcher, BaseCabhireQuoteFetcher, BaseRailQuoteFetcher, BaseIrlQuoteFetcher, BaseSearchDepotFetcher, BaseGetRailStationInfoFetcher, BaseGetRailStationFetcher, BaseGetIrlSupplierStationFetcher, BaseGetRiskAlertsFetcher, BaseSearchConfermaQuicklistFetcher, BaseGetUserRecentSearchesFetcher, BaseGetRailcardsFetcher, BaseGetCovidMicrositeTokenFetcher, BaseEurostarQuoteFetcher, BaseSearchUsersCanBookForFetcher, BaseSearchIrlStationsFetcher, BaseIrlDiscountCardFetcher, BaseGetFlightFareRulesFetcher, BaseGetFlightSeatMapFetcher, BaseGetRailLiveDeparturesFetcher, BaseHotelRulesFetcher, BaseApplyJitFlightRulesFetcher, BaseGetUserFavouriteSearchesFetcher, BaseDeleteFavouriteSearchUpdater, BaseEmailBasketFetcher, BaseFastTrackQuoteFetcher, BaseSendOfflineNotificationFetcher, BaseGenerateBasketPdfFetcher, BaseEmailFerryBookingFetcher, BaseGetFlightExtrasOptionsFetcher, BaseConvertCurrencyFetcher, BaseGetCurrencyConversionRatesFetcher, BaseGetHotelDetailsFetcher, BaseGetServiceFetcher, BaseGetFlightBrandedFaresFetcher, BaseGetTrainlineSearchConfigFetcher, BaseSearchRailInwardFetcher, BaseRailSearchExchangeFetcher, BaseCreateItineraryExchangeUpdater, BaseGetAirAvailabilityFetcher, BaseGetFlightAtNewClassFetcher, BaseGetFlightUpsellOffersFetcher, BaseGetOfficesFetcher, BaseFlightSearchExchangeFetcher, BaseFlightExchangeDetailsFetcher, BaseEmailApartmentBookingFetcher, BaseEmailSeasonTicketBookingFetcher, BaseSuggestPlacesFetcher, BaseGetLatLonFromHereIdFetcher, BaseGetGmtItineraryOptionsFetcher, BaseGetGutCitySuggestionsFetcher, BaseGetGutLocationSuggestionsFetcher, BaseGetRouteHappyFetcher, BaseGetMultipleHotelRatingFetcher, BaseEmailMeetingRoomBookingFetcher, BaseEmailAmbulanceBookingFetcher, BaseGetEntLocationByPostcode, BaseGetEntLocationByCity, BaseGetUserRecentBoltSearchesFetcher, BaseSaveRecentBoltSearchUpdater, BaseGetFerryPortsFetcher, BaseGetBannerFetcher, BaseUpdateDOBUpdater, BaseUpdateFlightSeatMapUpdater, BaseDeleteAllRecentSearchesUpdater, BaseGetOfficeDivisionsFetcher, BaseSearchGeoLocationFetcher, BaseSearchDiscoverLocationFetcher, BaseGetJyrneyUrlFetcher, BaseGetPriceHiddenSabreExchangeFetcher, BaseEnterpriseSearchService, BaseGetLatestVersionsFetcher, BaseLogonService, BaseCancelBookingUpdater, BaseAmendBookingUpdater, BaseCheckForDuplicateBookingsFetcher, BaseEnterpriseMyBookingsService, BaseRouteHappyService, BaseHttpCancelService, BaseMessagesService, RailFareNameKeywords, RailSplitType, isGroupTicket, RailFareTypes, SingleOrReturn, FlightSearchType, FlightDirectionEnum, BaseChatbotService, UserFavorurite, RailSearchCriteria, TimeWindow, LocationTypes, EurostarSearchType, ModalTypes, BaseGetMiRequiringMandatoryDefaultValueFetcher, BaseHttpCancelInterceptor } from '@sabstravtech/obtservices/base';
|
|
4
4
|
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
|
5
5
|
import * as i1 from 'apollo-angular';
|
|
6
6
|
import { gql } from 'apollo-angular';
|
|
7
|
-
import { Subscription, throwError, of, BehaviorSubject, Subject, forkJoin } from 'rxjs';
|
|
7
|
+
import { Subscription, throwError, of, BehaviorSubject, Subject, forkJoin, filter, take, timeout as timeout$1, catchError as catchError$1, EMPTY, firstValueFrom, skip, combineLatest, race, timer } from 'rxjs';
|
|
8
8
|
import moment$1 from 'moment';
|
|
9
9
|
import * as i1$1 from '@angular/common/http';
|
|
10
10
|
import { HttpHeaders } from '@angular/common/http';
|
|
11
11
|
import * as i2 from '@angular/router';
|
|
12
|
-
import { timeout, map, catchError, takeUntil } from 'rxjs/operators';
|
|
12
|
+
import { timeout, map, catchError, distinctUntilChanged, skipWhile, takeUntil } from 'rxjs/operators';
|
|
13
13
|
import jwt_decode from 'jwt-decode';
|
|
14
14
|
import * as i1$2 from '@angular/service-worker';
|
|
15
15
|
import * as _ from 'lodash';
|
|
@@ -2892,6 +2892,7 @@ const UserFieldsFragmentDoc = gql `
|
|
|
2892
2892
|
canSeeBookingsOfOthers
|
|
2893
2893
|
becomeUser
|
|
2894
2894
|
canOverrideApproval
|
|
2895
|
+
aiTravelAssistantEnabled
|
|
2895
2896
|
}
|
|
2896
2897
|
${UserSummaryFieldsFragmentDoc}`;
|
|
2897
2898
|
const AcceptNewPriceDocument = gql `
|
|
@@ -2915,6 +2916,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
2915
2916
|
providedIn: 'root'
|
|
2916
2917
|
}]
|
|
2917
2918
|
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
2919
|
+
const AcceptProcessTermsDocument = gql `
|
|
2920
|
+
mutation acceptProcessTerms($basketItemId: ID!, $newPrice: Float!) {
|
|
2921
|
+
acceptProcessTerms(basketItemId: $basketItemId, newPrice: $newPrice)
|
|
2922
|
+
}
|
|
2923
|
+
`;
|
|
2924
|
+
class AcceptProcessTermsGQL extends i1.Mutation {
|
|
2925
|
+
document = AcceptProcessTermsDocument;
|
|
2926
|
+
constructor(apollo) {
|
|
2927
|
+
super(apollo);
|
|
2928
|
+
}
|
|
2929
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AcceptProcessTermsGQL, deps: [{ token: i1.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2930
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AcceptProcessTermsGQL, providedIn: 'root' });
|
|
2931
|
+
}
|
|
2932
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AcceptProcessTermsGQL, decorators: [{
|
|
2933
|
+
type: Injectable,
|
|
2934
|
+
args: [{
|
|
2935
|
+
providedIn: 'root'
|
|
2936
|
+
}]
|
|
2937
|
+
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
2918
2938
|
const AddBasketItemCustomRemarkDocument = gql `
|
|
2919
2939
|
mutation addBasketItemCustomRemark($basketItem: ID!, $remark_type: String!, $text: String!) {
|
|
2920
2940
|
addBasketItemCustomRemark(
|
|
@@ -4639,6 +4659,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
4639
4659
|
providedIn: 'root'
|
|
4640
4660
|
}]
|
|
4641
4661
|
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
4662
|
+
const DownloadETicketDocument = gql `
|
|
4663
|
+
query downloadETicket($url: String!) {
|
|
4664
|
+
downloadETicket(url: $url) {
|
|
4665
|
+
content
|
|
4666
|
+
contentType
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
`;
|
|
4670
|
+
class DownloadETicketGQL extends i1.Query {
|
|
4671
|
+
document = DownloadETicketDocument;
|
|
4672
|
+
constructor(apollo) {
|
|
4673
|
+
super(apollo);
|
|
4674
|
+
}
|
|
4675
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadETicketGQL, deps: [{ token: i1.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4676
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadETicketGQL, providedIn: 'root' });
|
|
4677
|
+
}
|
|
4678
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadETicketGQL, decorators: [{
|
|
4679
|
+
type: Injectable,
|
|
4680
|
+
args: [{
|
|
4681
|
+
providedIn: 'root'
|
|
4682
|
+
}]
|
|
4683
|
+
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
4642
4684
|
const EditUserDocument = gql `
|
|
4643
4685
|
mutation editUser($title: String, $forename: String, $surname: String, $email: String, $additionalEmail: [String], $dob: Date, $selectedLanguage: ID, $emergencyContactName: String, $emergencyContactPhone: String, $emergencyContactEmail: String, $phoneNumbers: [PhoneNumberInput!], $addresses: [AddressInput!], $documents: [DocumentInput!], $loyaltyCodes: [UserLoyaltyCodeInput!], $preferences: [UserPreferenceInput!], $userApprovers: [UserApproverInput!], $eticketEmail: String) {
|
|
4644
4686
|
editUser(
|
|
@@ -7992,6 +8034,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
7992
8034
|
providedIn: 'root'
|
|
7993
8035
|
}]
|
|
7994
8036
|
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
8037
|
+
const GetJyrneyUrlDocument = gql `
|
|
8038
|
+
query getJyrneyUrl {
|
|
8039
|
+
getJyrneyUrl
|
|
8040
|
+
}
|
|
8041
|
+
`;
|
|
8042
|
+
class GetJyrneyUrlGQL extends i1.Query {
|
|
8043
|
+
document = GetJyrneyUrlDocument;
|
|
8044
|
+
constructor(apollo) {
|
|
8045
|
+
super(apollo);
|
|
8046
|
+
}
|
|
8047
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetJyrneyUrlGQL, deps: [{ token: i1.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8048
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetJyrneyUrlGQL, providedIn: 'root' });
|
|
8049
|
+
}
|
|
8050
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetJyrneyUrlGQL, decorators: [{
|
|
8051
|
+
type: Injectable,
|
|
8052
|
+
args: [{
|
|
8053
|
+
providedIn: 'root'
|
|
8054
|
+
}]
|
|
8055
|
+
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
7995
8056
|
const GetLatLonFromHereIdDocument = gql `
|
|
7996
8057
|
query getLatLonFromHereID($id: String!) {
|
|
7997
8058
|
getLatLonFromHereID(id: $id) {
|
|
@@ -8271,6 +8332,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
8271
8332
|
providedIn: 'root'
|
|
8272
8333
|
}]
|
|
8273
8334
|
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
8335
|
+
const GetProcessTermsPriceDocument = gql `
|
|
8336
|
+
query getProcessTermsPrice($basketItemId: ID!, $supplementaryInfo: [SupplementaryBookingInfo!]) {
|
|
8337
|
+
getProcessTermsPrice(
|
|
8338
|
+
basketItemId: $basketItemId
|
|
8339
|
+
supplementaryInfo: $supplementaryInfo
|
|
8340
|
+
) {
|
|
8341
|
+
amount
|
|
8342
|
+
currency
|
|
8343
|
+
taxItemsWithoutCode {
|
|
8344
|
+
name
|
|
8345
|
+
amount
|
|
8346
|
+
currency
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8349
|
+
}
|
|
8350
|
+
`;
|
|
8351
|
+
class GetProcessTermsPriceGQL extends i1.Query {
|
|
8352
|
+
document = GetProcessTermsPriceDocument;
|
|
8353
|
+
constructor(apollo) {
|
|
8354
|
+
super(apollo);
|
|
8355
|
+
}
|
|
8356
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetProcessTermsPriceGQL, deps: [{ token: i1.Apollo }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8357
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetProcessTermsPriceGQL, providedIn: 'root' });
|
|
8358
|
+
}
|
|
8359
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetProcessTermsPriceGQL, decorators: [{
|
|
8360
|
+
type: Injectable,
|
|
8361
|
+
args: [{
|
|
8362
|
+
providedIn: 'root'
|
|
8363
|
+
}]
|
|
8364
|
+
}], ctorParameters: () => [{ type: i1.Apollo }] });
|
|
8274
8365
|
const GetRailLiveDeparturesDocument = gql `
|
|
8275
8366
|
query getRailLiveDepartures($stationCode: String!, $departDateTime: String, $destinationCode: String) {
|
|
8276
8367
|
getRailLiveDepartures(
|
|
@@ -12626,7 +12717,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12626
12717
|
multiPaymentGroup.push(next);
|
|
12627
12718
|
}
|
|
12628
12719
|
else {
|
|
12629
|
-
const defaultPayment = next.availablePaymentMethods.find(payment => payment.name === PaymentMethodType.OnAccount);
|
|
12720
|
+
const defaultPayment = next.availablePaymentMethods.find((payment) => payment.name === PaymentMethodType.OnAccount);
|
|
12630
12721
|
if (defaultPayment) {
|
|
12631
12722
|
current[index].setValue(defaultPayment);
|
|
12632
12723
|
}
|
|
@@ -12636,54 +12727,96 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12636
12727
|
return current;
|
|
12637
12728
|
}, {}))
|
|
12638
12729
|
};
|
|
12639
|
-
return suppInfoArray
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
if (next.perItemPhoneNumber) {
|
|
12645
|
-
newItems.perItemPhoneNumber = this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12646
|
-
}
|
|
12647
|
-
if (next?.perItemSupplementarySchema && Object.keys(next.perItemSupplementarySchema).length) {
|
|
12648
|
-
newItems.perItemSupplementarySchema = this.makeSchema(next.perItemSupplementarySchema);
|
|
12649
|
-
}
|
|
12650
|
-
next.userInfo.reduce((currentUsers, user, userIndex) => {
|
|
12651
|
-
const userControls = {};
|
|
12652
|
-
if (next.perPassengerAddress) {
|
|
12653
|
-
userControls.perPassengerAddress = this.addAddressGroup();
|
|
12654
|
-
}
|
|
12655
|
-
// ENT-15831: always create the phone group for flights so the field can render
|
|
12656
|
-
// even when the basket response omits perPassengerPhoneNumber (e.g. setBasketItemPaymentMethod)
|
|
12657
|
-
if (next.perPassengerPhoneNumber || basketItems[index]?.service?.type === ServiceType.Flight) {
|
|
12658
|
-
userControls.perPassengerPhoneNumber =
|
|
12659
|
-
this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12660
|
-
}
|
|
12661
|
-
if (next.perPassengerSupplementaryBookingInfoSchema && Object.keys(next.perPassengerSupplementaryBookingInfoSchema).length) {
|
|
12662
|
-
userControls.perPassengerSupplementaryBookingInfoSchema = this.makeSchema(next.perPassengerSupplementaryBookingInfoSchema);
|
|
12730
|
+
return suppInfoArray
|
|
12731
|
+
? suppInfoArray.reduce((current, next, index) => {
|
|
12732
|
+
const newItems = {};
|
|
12733
|
+
if (next.perItemAddress) {
|
|
12734
|
+
newItems.perItemAddress = this.addAddressGroup();
|
|
12663
12735
|
}
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
}, newItems);
|
|
12667
|
-
next.guestInfo.reduce((currentUsers, user, userIndex) => {
|
|
12668
|
-
const userControls = {};
|
|
12669
|
-
if (next.perPassengerAddress) {
|
|
12670
|
-
userControls.perPassengerAddress = this.addAddressGroup();
|
|
12736
|
+
if (next.perItemPhoneNumber) {
|
|
12737
|
+
newItems.perItemPhoneNumber = this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12671
12738
|
}
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
userControls.perPassengerPhoneNumber =
|
|
12676
|
-
this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12739
|
+
if (next?.perItemSupplementarySchema &&
|
|
12740
|
+
Object.keys(next.perItemSupplementarySchema).length) {
|
|
12741
|
+
newItems.perItemSupplementarySchema = this.makeSchema(next.perItemSupplementarySchema);
|
|
12677
12742
|
}
|
|
12678
|
-
|
|
12679
|
-
userControls
|
|
12680
|
-
|
|
12681
|
-
|
|
12682
|
-
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12743
|
+
next.userInfo.reduce((currentUsers, user, userIndex) => {
|
|
12744
|
+
const userControls = {};
|
|
12745
|
+
if (next.perPassengerAddress) {
|
|
12746
|
+
userControls.perPassengerAddress = this.addAddressGroup();
|
|
12747
|
+
}
|
|
12748
|
+
// ENT-15831: always create the phone group for flights so the field can render
|
|
12749
|
+
// even when the basket response omits perPassengerPhoneNumber (e.g. setBasketItemPaymentMethod)
|
|
12750
|
+
if (next.perPassengerPhoneNumber ||
|
|
12751
|
+
basketItems[index]?.service?.type === ServiceType.Flight) {
|
|
12752
|
+
userControls.perPassengerPhoneNumber =
|
|
12753
|
+
this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12754
|
+
}
|
|
12755
|
+
if (next.perItemPhoneNumber) {
|
|
12756
|
+
newItems.perItemPhoneNumber = this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12757
|
+
}
|
|
12758
|
+
if (next?.perItemSupplementarySchema &&
|
|
12759
|
+
Object.keys(next.perItemSupplementarySchema).length) {
|
|
12760
|
+
newItems.perItemSupplementarySchema = this.makeSchema(next.perItemSupplementarySchema);
|
|
12761
|
+
}
|
|
12762
|
+
next.userInfo.reduce((currentUsers, user, userIndex) => {
|
|
12763
|
+
const userControls = {};
|
|
12764
|
+
if (next.perPassengerAddress) {
|
|
12765
|
+
userControls.perPassengerAddress = this.addAddressGroup();
|
|
12766
|
+
}
|
|
12767
|
+
if (next.perPassengerPhoneNumber) {
|
|
12768
|
+
userControls.perPassengerPhoneNumber =
|
|
12769
|
+
this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12770
|
+
}
|
|
12771
|
+
if (next.perPassengerSupplementaryBookingInfoSchema &&
|
|
12772
|
+
Object.keys(next.perPassengerSupplementaryBookingInfoSchema).length) {
|
|
12773
|
+
userControls.perPassengerSupplementaryBookingInfoSchema = this.makeSchema(next.perPassengerSupplementaryBookingInfoSchema);
|
|
12774
|
+
}
|
|
12775
|
+
currentUsers['user_' + userIndex] = new FormGroup(userControls);
|
|
12776
|
+
return currentUsers;
|
|
12777
|
+
}, newItems);
|
|
12778
|
+
next.guestInfo.reduce((currentUsers, user, userIndex) => {
|
|
12779
|
+
const userControls = {};
|
|
12780
|
+
if (next.perPassengerAddress) {
|
|
12781
|
+
userControls.perPassengerAddress = this.addAddressGroup();
|
|
12782
|
+
}
|
|
12783
|
+
if (next.perPassengerPhoneNumber) {
|
|
12784
|
+
userControls.perPassengerPhoneNumber =
|
|
12785
|
+
this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12786
|
+
}
|
|
12787
|
+
if (next.perPassengerSupplementaryBookingInfoSchema &&
|
|
12788
|
+
Object.keys(next.perPassengerSupplementaryBookingInfoSchema).length) {
|
|
12789
|
+
userControls.perPassengerSupplementaryBookingInfoSchema = this.makeSchema(next.perPassengerSupplementaryBookingInfoSchema);
|
|
12790
|
+
}
|
|
12791
|
+
currentUsers['guest_' + userIndex] = new FormGroup(userControls);
|
|
12792
|
+
return currentUsers;
|
|
12793
|
+
}, newItems);
|
|
12794
|
+
current['basketItem_' + index] = new FormGroup(newItems);
|
|
12795
|
+
return current;
|
|
12796
|
+
}, newItems);
|
|
12797
|
+
next.guestInfo.reduce((currentUsers, user, userIndex) => {
|
|
12798
|
+
const userControls = {};
|
|
12799
|
+
if (next.perPassengerAddress) {
|
|
12800
|
+
userControls.perPassengerAddress = this.addAddressGroup();
|
|
12801
|
+
}
|
|
12802
|
+
// ENT-15831: always create the phone group for flights so the field can render
|
|
12803
|
+
// even when the basket response omits perPassengerPhoneNumber (e.g. setBasketItemPaymentMethod)
|
|
12804
|
+
if (next.perPassengerPhoneNumber ||
|
|
12805
|
+
basketItems[index]?.service?.type === ServiceType.Flight) {
|
|
12806
|
+
userControls.perPassengerPhoneNumber =
|
|
12807
|
+
this.addPhoneNumberGroup(checkPhoneNumberValidator);
|
|
12808
|
+
}
|
|
12809
|
+
if (next.perPassengerSupplementaryBookingInfoSchema &&
|
|
12810
|
+
Object.keys(next.perPassengerSupplementaryBookingInfoSchema).length) {
|
|
12811
|
+
userControls.perPassengerSupplementaryBookingInfoSchema = this.makeSchema(next.perPassengerSupplementaryBookingInfoSchema);
|
|
12812
|
+
}
|
|
12813
|
+
currentUsers['guest_' + userIndex] = new FormGroup(userControls);
|
|
12814
|
+
return currentUsers;
|
|
12815
|
+
}, newItems);
|
|
12816
|
+
current['basketItem_' + index] = new FormGroup(newItems);
|
|
12817
|
+
return current;
|
|
12818
|
+
}, paymentMethods)
|
|
12819
|
+
: {};
|
|
12687
12820
|
}
|
|
12688
12821
|
addAddressGroup() {
|
|
12689
12822
|
return new FormGroup({
|
|
@@ -12711,10 +12844,10 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12711
12844
|
if (value.type === SchemaFormat.object) {
|
|
12712
12845
|
current[key] = this.makeSchema(value);
|
|
12713
12846
|
}
|
|
12714
|
-
else if ((data?.required?.includes(key) || key ===
|
|
12847
|
+
else if ((data?.required?.includes(key) || key === 'ticketDeliveryOption') &&
|
|
12715
12848
|
value.type !== SchemaFormat.boolean &&
|
|
12716
12849
|
key !== SchemaFormat.passport) {
|
|
12717
|
-
|
|
12850
|
+
const validators = [Validators.required];
|
|
12718
12851
|
if (value.pattern) {
|
|
12719
12852
|
validators.push(Validators.pattern(value.pattern));
|
|
12720
12853
|
}
|
|
@@ -12724,7 +12857,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12724
12857
|
value.oneOf.forEach((oneOf) => {
|
|
12725
12858
|
const newFormControl = this.makeSchema(oneOf);
|
|
12726
12859
|
const controlKeys = Object.keys(newFormControl.controls);
|
|
12727
|
-
controlKeys.forEach(key => {
|
|
12860
|
+
controlKeys.forEach((key) => {
|
|
12728
12861
|
if (key === 'dateOfIssue' || key === 'dateOfExpiry') {
|
|
12729
12862
|
// ! Do this otherwise ngbdate registers as invalid by default, even when no required
|
|
12730
12863
|
newFormControl.controls[key].setValue(null);
|
|
@@ -12788,7 +12921,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12788
12921
|
}
|
|
12789
12922
|
isOnStaging() {
|
|
12790
12923
|
return !!['test'].find((url) => {
|
|
12791
|
-
return window.location.hostname.includes(url) && !window.location.hostname.includes('mistest');
|
|
12924
|
+
return (window.location.hostname.includes(url) && !window.location.hostname.includes('mistest'));
|
|
12792
12925
|
});
|
|
12793
12926
|
}
|
|
12794
12927
|
isOnDev() {
|
|
@@ -12799,7 +12932,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12799
12932
|
createPaymentGroup(multiPaymentGroup) {
|
|
12800
12933
|
return multiPaymentGroup.reduce((acc, basketItem) => {
|
|
12801
12934
|
if (acc.length) {
|
|
12802
|
-
const dupe = acc.find(item => item.paymentMethod === basketItem.availablePaymentMethods[0].name);
|
|
12935
|
+
const dupe = acc.find((item) => item.paymentMethod === basketItem.availablePaymentMethods[0].name);
|
|
12803
12936
|
if (dupe) {
|
|
12804
12937
|
dupe.types.push(basketItem.service.type);
|
|
12805
12938
|
}
|
|
@@ -12829,8 +12962,8 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12829
12962
|
attemptToFillInAddress(userAddresses = [], addresses = [], form) {
|
|
12830
12963
|
try {
|
|
12831
12964
|
// first of all, try to find the User primary address
|
|
12832
|
-
const userPrimary = userAddresses.find(addr => addr.userPrimary);
|
|
12833
|
-
const officePrimary = addresses.find(addr => addr.officePrimary);
|
|
12965
|
+
const userPrimary = userAddresses.find((addr) => addr.userPrimary);
|
|
12966
|
+
const officePrimary = addresses.find((addr) => addr.officePrimary);
|
|
12834
12967
|
// if found - fill in the address form with that
|
|
12835
12968
|
// if not - use the first user addresses
|
|
12836
12969
|
// if not - use Office Primary
|
|
@@ -12860,7 +12993,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12860
12993
|
return form;
|
|
12861
12994
|
}
|
|
12862
12995
|
const keys = Object.keys(form.controls);
|
|
12863
|
-
keys.forEach(key => {
|
|
12996
|
+
keys.forEach((key) => {
|
|
12864
12997
|
const control = form.controls[key];
|
|
12865
12998
|
// console.log(control);
|
|
12866
12999
|
if (control['controls']?.perItemSupplementarySchema) {
|
|
@@ -12902,13 +13035,13 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12902
13035
|
attemptToFillInParkingMobileNumber(data, form) {
|
|
12903
13036
|
try {
|
|
12904
13037
|
const outerKeys = Object.keys(form.controls);
|
|
12905
|
-
outerKeys.forEach(outerKey => {
|
|
13038
|
+
outerKeys.forEach((outerKey) => {
|
|
12906
13039
|
const formOuterKey = form.controls[outerKey];
|
|
12907
13040
|
const formPerItemSupplementarySchema = formOuterKey.controls
|
|
12908
13041
|
.perItemSupplementarySchema;
|
|
12909
13042
|
if (formOuterKey.controls.perItemSupplementarySchema) {
|
|
12910
13043
|
const keys = Object.keys(formPerItemSupplementarySchema.controls);
|
|
12911
|
-
keys.forEach(key => {
|
|
13044
|
+
keys.forEach((key) => {
|
|
12912
13045
|
if (key === 'mobileNum') {
|
|
12913
13046
|
// now grab the mobile number from the data
|
|
12914
13047
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -12927,7 +13060,8 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12927
13060
|
break;
|
|
12928
13061
|
}
|
|
12929
13062
|
const phone = userPrimaryPhone || data[0].phoneNumbers[0];
|
|
12930
|
-
const telephone = formPerItemSupplementarySchema.controls
|
|
13063
|
+
const telephone = formPerItemSupplementarySchema.controls
|
|
13064
|
+
?.car3_telephone;
|
|
12931
13065
|
if (phone) {
|
|
12932
13066
|
telephone.controls?.TelCountryCode.setValue(`${phone.countryCode}`);
|
|
12933
13067
|
telephone.controls?.TelNumber.setValue(`${phone.number}`);
|
|
@@ -12948,7 +13082,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12948
13082
|
attemptToFillInDOB(data, index, form, isGuestUser = false) {
|
|
12949
13083
|
const outerKeys = Object.keys(form.controls);
|
|
12950
13084
|
const userIndex = isGuestUser ? `guest_${index}` : `user_${index}`;
|
|
12951
|
-
outerKeys.forEach(outerKey => {
|
|
13085
|
+
outerKeys.forEach((outerKey) => {
|
|
12952
13086
|
const formOuterKey = form.controls[outerKey];
|
|
12953
13087
|
const formUserIndex = formOuterKey.controls[userIndex];
|
|
12954
13088
|
const formPerPassengerSupplementaryBookingInfoSchema = formUserIndex?.controls
|
|
@@ -12969,14 +13103,15 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
12969
13103
|
attemptToFillInMeal(data, index, form, isGuestUser = false) {
|
|
12970
13104
|
const outerKeys = Object.keys(form.controls);
|
|
12971
13105
|
const userIndex = isGuestUser ? `guest_${index}` : `user_${index}`;
|
|
12972
|
-
outerKeys.forEach(outerKey => {
|
|
13106
|
+
outerKeys.forEach((outerKey) => {
|
|
12973
13107
|
const formOuterKey = form.controls[outerKey];
|
|
12974
13108
|
const formUserIndex = formOuterKey.controls[userIndex];
|
|
12975
13109
|
const formPerPassengerSupplementaryBookingInfoSchema = formUserIndex?.controls
|
|
12976
13110
|
?.perPassengerSupplementaryBookingInfoSchema;
|
|
12977
|
-
if (formPerPassengerSupplementaryBookingInfoSchema?.controls?.meal &&
|
|
13111
|
+
if (formPerPassengerSupplementaryBookingInfoSchema?.controls?.meal &&
|
|
13112
|
+
data?.preferences?.length) {
|
|
12978
13113
|
const meal = formPerPassengerSupplementaryBookingInfoSchema.controls.meal;
|
|
12979
|
-
const mealPref = data.preferences.find(pref => pref.preferenceKey === 'MEAL_REQUEST');
|
|
13114
|
+
const mealPref = data.preferences.find((pref) => pref.preferenceKey === 'MEAL_REQUEST');
|
|
12980
13115
|
if (mealPref) {
|
|
12981
13116
|
const mealPrefVal = mealPref.preferenceValue?.match(/(?<=\()(.*?)(?=\))/g);
|
|
12982
13117
|
meal.setValue(mealPrefVal ? mealPrefVal[0] : '');
|
|
@@ -13019,7 +13154,8 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13019
13154
|
const keys = Object.keys(form.controls);
|
|
13020
13155
|
for (const key of keys) {
|
|
13021
13156
|
const controls = form.controls[key];
|
|
13022
|
-
if (key.includes(substring)) {
|
|
13157
|
+
if (key.includes(substring)) {
|
|
13158
|
+
// could possibly change this to a regex
|
|
13023
13159
|
found.push({ controls, key }); // needs the key so we can identify the appropriate mi for pnr
|
|
13024
13160
|
}
|
|
13025
13161
|
if (controls.controls) {
|
|
@@ -13050,7 +13186,8 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13050
13186
|
found.push({ controls, key }); // needs the key so we can identify the appropriate mi for pnr
|
|
13051
13187
|
}
|
|
13052
13188
|
}
|
|
13053
|
-
else if (key === string) {
|
|
13189
|
+
else if (key === string) {
|
|
13190
|
+
// could possibly change this to a regex
|
|
13054
13191
|
found.push({ controls, key }); // needs the key so we can identify the appropriate mi for pnr
|
|
13055
13192
|
}
|
|
13056
13193
|
if (controls.controls) {
|
|
@@ -13070,7 +13207,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13070
13207
|
*/
|
|
13071
13208
|
updateFormValueWithCloneValue(clonedControl, matchKey, form) {
|
|
13072
13209
|
const keys = Object.keys(form.controls);
|
|
13073
|
-
for (
|
|
13210
|
+
for (const key of keys) {
|
|
13074
13211
|
let controls = form.controls[key];
|
|
13075
13212
|
const splitString = matchKey.split('-');
|
|
13076
13213
|
const splitkey = key.split('-');
|
|
@@ -13108,7 +13245,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13108
13245
|
return basketItems.reduce((bookingInfo, item) => {
|
|
13109
13246
|
const userArray = this.createBlankUserSupp(item.users);
|
|
13110
13247
|
const guestArray = this.createBlankUserSupp(item.guests);
|
|
13111
|
-
|
|
13248
|
+
const info = {
|
|
13112
13249
|
basketItem: item.id,
|
|
13113
13250
|
guestInfo: guestArray,
|
|
13114
13251
|
paymentMethodDetails: {},
|
|
@@ -13120,7 +13257,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13120
13257
|
}, []);
|
|
13121
13258
|
}
|
|
13122
13259
|
createBlankUserSupp(users) {
|
|
13123
|
-
return users.map(user => {
|
|
13260
|
+
return users.map((user) => {
|
|
13124
13261
|
return {
|
|
13125
13262
|
id: user.id,
|
|
13126
13263
|
supplementaryInfo: {}
|
|
@@ -13135,8 +13272,8 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13135
13272
|
return basketItems.map((item) => {
|
|
13136
13273
|
const info = {
|
|
13137
13274
|
basketItem: item.id,
|
|
13138
|
-
userInfo: (item.users || []).map(user => this.buildUserInfoFromSaved(user)),
|
|
13139
|
-
guestInfo: (item.guests || []).map(guest => this.buildUserInfoFromSaved(guest)),
|
|
13275
|
+
userInfo: (item.users || []).map((user) => this.buildUserInfoFromSaved(user)),
|
|
13276
|
+
guestInfo: (item.guests || []).map((guest) => this.buildUserInfoFromSaved(guest)),
|
|
13140
13277
|
supplementaryInfo: item.savedSupplementaryInfo || {},
|
|
13141
13278
|
paymentMethodDetails: {}
|
|
13142
13279
|
};
|
|
@@ -13178,11 +13315,11 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13178
13315
|
};
|
|
13179
13316
|
}
|
|
13180
13317
|
checkBasketItemHasService(basket) {
|
|
13181
|
-
return !basket.basketItems.find(item => !item.service);
|
|
13318
|
+
return !basket.basketItems.find((item) => !item.service);
|
|
13182
13319
|
}
|
|
13183
13320
|
getNamedPropertyFromObject(obj, lookupKey) {
|
|
13184
13321
|
const keys = Object.keys(obj);
|
|
13185
|
-
for (
|
|
13322
|
+
for (const key of keys) {
|
|
13186
13323
|
if (key === lookupKey) {
|
|
13187
13324
|
//@ts-ignore
|
|
13188
13325
|
return obj[key];
|
|
@@ -13191,7 +13328,8 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13191
13328
|
else if (typeof obj[key] === 'object') {
|
|
13192
13329
|
//@ts-ignore
|
|
13193
13330
|
const found = this.getNamedPropertyFromObject(obj[key], lookupKey);
|
|
13194
|
-
if (found) {
|
|
13331
|
+
if (found) {
|
|
13332
|
+
// else continue
|
|
13195
13333
|
return found;
|
|
13196
13334
|
}
|
|
13197
13335
|
}
|
|
@@ -13199,9 +13337,9 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13199
13337
|
return null;
|
|
13200
13338
|
}
|
|
13201
13339
|
getAllKeysFromObject(obj, includeParentObjectKeys = false) {
|
|
13202
|
-
|
|
13340
|
+
const keys = Object.keys(obj);
|
|
13203
13341
|
let returnKeys = [];
|
|
13204
|
-
for (
|
|
13342
|
+
for (const key of keys) {
|
|
13205
13343
|
//@ts-ignore
|
|
13206
13344
|
if (typeof obj[key] === 'object') {
|
|
13207
13345
|
if (includeParentObjectKeys) {
|
|
@@ -13218,7 +13356,7 @@ class HelperRoutines extends BaseHelperRoutines {
|
|
|
13218
13356
|
}
|
|
13219
13357
|
getAllControlKeysFromForm(form) {
|
|
13220
13358
|
let keys = Object.keys(form.controls);
|
|
13221
|
-
for (
|
|
13359
|
+
for (const key of keys) {
|
|
13222
13360
|
const control = form.controls[key];
|
|
13223
13361
|
if (control.controls) {
|
|
13224
13362
|
keys = keys.concat(this.getAllControlKeysFromForm(control));
|
|
@@ -13811,8 +13949,7 @@ class WebTokenService extends BaseWebTokenService {
|
|
|
13811
13949
|
}
|
|
13812
13950
|
};
|
|
13813
13951
|
// Warn if the browser doesn't support addEventListener or the Page Visibility API
|
|
13814
|
-
if (typeof document.addEventListener === 'undefined' ||
|
|
13815
|
-
hidden === undefined) {
|
|
13952
|
+
if (typeof document.addEventListener === 'undefined' || hidden === undefined) {
|
|
13816
13953
|
console.log('This demo requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API.');
|
|
13817
13954
|
}
|
|
13818
13955
|
else {
|
|
@@ -14362,6 +14499,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
14362
14499
|
args: [HelperRoutines]
|
|
14363
14500
|
}] }, { type: ModalOpenerService }] });
|
|
14364
14501
|
|
|
14502
|
+
class GetProcessTermsPriceFetcher extends BaseGetProcessTermsPriceFetcher {
|
|
14503
|
+
constructor(queryGQL, helpers, modalService) {
|
|
14504
|
+
super(queryGQL, helpers, modalService);
|
|
14505
|
+
}
|
|
14506
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetProcessTermsPriceFetcher, deps: [{ token: GetProcessTermsPriceGQL }, { token: HelperRoutines }, { token: ModalOpenerService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
14507
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetProcessTermsPriceFetcher, providedIn: 'root' });
|
|
14508
|
+
}
|
|
14509
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetProcessTermsPriceFetcher, decorators: [{
|
|
14510
|
+
type: Injectable,
|
|
14511
|
+
args: [{ providedIn: 'root' }]
|
|
14512
|
+
}], ctorParameters: () => [{ type: GetProcessTermsPriceGQL, decorators: [{
|
|
14513
|
+
type: Inject,
|
|
14514
|
+
args: [GetProcessTermsPriceGQL]
|
|
14515
|
+
}] }, { type: HelperRoutines, decorators: [{
|
|
14516
|
+
type: Inject,
|
|
14517
|
+
args: [HelperRoutines]
|
|
14518
|
+
}] }, { type: ModalOpenerService }] });
|
|
14519
|
+
|
|
14520
|
+
class AcceptProcessTermsUpdater extends BaseAcceptProcessTermsUpdater {
|
|
14521
|
+
constructor(updater, modalService) {
|
|
14522
|
+
super(updater, modalService);
|
|
14523
|
+
}
|
|
14524
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AcceptProcessTermsUpdater, deps: [{ token: AcceptProcessTermsGQL }, { token: ModalOpenerService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
14525
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AcceptProcessTermsUpdater, providedIn: 'root' });
|
|
14526
|
+
}
|
|
14527
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: AcceptProcessTermsUpdater, decorators: [{
|
|
14528
|
+
type: Injectable,
|
|
14529
|
+
args: [{ providedIn: 'root' }]
|
|
14530
|
+
}], ctorParameters: () => [{ type: AcceptProcessTermsGQL }, { type: ModalOpenerService }] });
|
|
14531
|
+
|
|
14365
14532
|
class ResendApproverEmailFetcher extends BaseResendApproverEmailFetcher {
|
|
14366
14533
|
constructor(queryGQL, helpers, modalService) {
|
|
14367
14534
|
super(queryGQL, helpers, modalService);
|
|
@@ -14716,19 +14883,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
14716
14883
|
args: [UpdateExchangeBasketGQL]
|
|
14717
14884
|
}] }, { type: ModalOpenerService }] });
|
|
14718
14885
|
|
|
14886
|
+
class DownloadETicketFetcher extends BaseDownloadETicketFetcher {
|
|
14887
|
+
constructor(queryGQL, helpers, modalService) {
|
|
14888
|
+
super(queryGQL, helpers, modalService);
|
|
14889
|
+
}
|
|
14890
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadETicketFetcher, deps: [{ token: DownloadETicketGQL }, { token: HelperRoutines }, { token: ModalOpenerService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
14891
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadETicketFetcher, providedIn: 'root' });
|
|
14892
|
+
}
|
|
14893
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: DownloadETicketFetcher, decorators: [{
|
|
14894
|
+
type: Injectable,
|
|
14895
|
+
args: [{ providedIn: 'root' }]
|
|
14896
|
+
}], ctorParameters: () => [{ type: DownloadETicketGQL, decorators: [{
|
|
14897
|
+
type: Inject,
|
|
14898
|
+
args: [DownloadETicketGQL]
|
|
14899
|
+
}] }, { type: HelperRoutines, decorators: [{
|
|
14900
|
+
type: Inject,
|
|
14901
|
+
args: [HelperRoutines]
|
|
14902
|
+
}] }, { type: ModalOpenerService }] });
|
|
14903
|
+
|
|
14719
14904
|
const moment = moment$1;
|
|
14720
14905
|
const TEMPID = 'TEMPID_';
|
|
14721
14906
|
class EnterpriseBasketService extends BaseEnterpriseBasketService {
|
|
14722
|
-
constructor(acceptNewPriceUpdater, addItemToBasketUpdater, addUserToBasketItem, basketCreator, basketFetcher, bookBasketUpdater, deleteBasketUpdater, helpers, removeItemFromBasketUpdater, revalidateBasketUpdater, getUserFetcher, getUserBasketsFetcher, setBasketItemLeadPassengerUpdater, moveItemToADifferentBasket, updateBasketNotes, updateBasketTitle, userService, modalService, setBasketItemPaymentOption, getCancellationFetcher, addGuestToBasketItem, addUpdatedItemToBasketUpdater, createBasketNote, getBasketNotes, getBasketApprovalTimestampsGQL, addBasketItemMarkupUpdater, addBasketItemCustomRemark, getBasketItemCustomRemarks, changeBasketOwnership, getBasketQuote, resendApproverEmail, basketRequiresApproval, getOfficeApproversFetcher, selectBasketApproverUpdater, getDraftBasketsFetcher, createBasketFromDraftUpdater, selectBasketNotifyApproverUpdater, canAmendBookingFetcher, getUserApproversFetcher, getMIApproversFetcher, getBasketApprovalInfoFetcher, shareBasketUpdater, getUserSharedBasketsFetcher, getTrainSeatMapFetcher, beforeAmendCabSearchQuoteFetcher, beforeAmendCarHireSearchfetcher, storageService, sendBackToQueueUpdater, basketCo2Fetcher, selectBasketMultiLevelApproversUpdater, validateBasketMiFetcher, validateBasketItemMiFetcher, setMIValuesUpdater, searchMIAutoSuggestValuesFetcher, getTrainSeatmapEvolviFetcher, reserveRailSeatsUpdater, evolviSeatmapsAreEnabledFetcher, getRequestedBookingUpdateFetcher, updateExchangeBasketUpdater) {
|
|
14723
|
-
super(acceptNewPriceUpdater, addItemToBasketUpdater, addUserToBasketItem, basketCreator, basketFetcher, bookBasketUpdater, deleteBasketUpdater, helpers, removeItemFromBasketUpdater, revalidateBasketUpdater, getUserFetcher, getUserBasketsFetcher, setBasketItemLeadPassengerUpdater, moveItemToADifferentBasket, updateBasketNotes, updateBasketTitle, setBasketItemPaymentOption, getCancellationFetcher, userService, modalService, addGuestToBasketItem, addUpdatedItemToBasketUpdater, createBasketNote, getBasketNotes, getBasketApprovalTimestampsGQL, addBasketItemMarkupUpdater, addBasketItemCustomRemark, getBasketItemCustomRemarks, changeBasketOwnership, getBasketQuote, resendApproverEmail, basketRequiresApproval, getOfficeApproversFetcher, selectBasketApproverUpdater, getDraftBasketsFetcher, createBasketFromDraftUpdater, selectBasketNotifyApproverUpdater, canAmendBookingFetcher, getUserApproversFetcher, getMIApproversFetcher, getBasketApprovalInfoFetcher, shareBasketUpdater, getUserSharedBasketsFetcher, getTrainSeatMapFetcher, beforeAmendCabSearchQuoteFetcher, beforeAmendCarHireSearchfetcher, storageService, sendBackToQueueUpdater, basketCo2Fetcher, selectBasketMultiLevelApproversUpdater, validateBasketMiFetcher, validateBasketItemMiFetcher, setMIValuesUpdater, searchMIAutoSuggestValuesFetcher, getTrainSeatmapEvolviFetcher, reserveRailSeatsUpdater, evolviSeatmapsAreEnabledFetcher, getRequestedBookingUpdateFetcher, updateExchangeBasketUpdater);
|
|
14907
|
+
constructor(acceptNewPriceUpdater, addItemToBasketUpdater, addUserToBasketItem, basketCreator, basketFetcher, bookBasketUpdater, deleteBasketUpdater, helpers, removeItemFromBasketUpdater, revalidateBasketUpdater, getUserFetcher, getUserBasketsFetcher, setBasketItemLeadPassengerUpdater, moveItemToADifferentBasket, updateBasketNotes, updateBasketTitle, userService, modalService, setBasketItemPaymentOption, getCancellationFetcher, addGuestToBasketItem, addUpdatedItemToBasketUpdater, createBasketNote, getBasketNotes, getBasketApprovalTimestampsGQL, addBasketItemMarkupUpdater, addBasketItemCustomRemark, getBasketItemCustomRemarks, changeBasketOwnership, getBasketQuote, getProcessTermsPrice, acceptProcessTermsUpdater, resendApproverEmail, basketRequiresApproval, getOfficeApproversFetcher, selectBasketApproverUpdater, getDraftBasketsFetcher, createBasketFromDraftUpdater, selectBasketNotifyApproverUpdater, canAmendBookingFetcher, getUserApproversFetcher, getMIApproversFetcher, getBasketApprovalInfoFetcher, shareBasketUpdater, getUserSharedBasketsFetcher, getTrainSeatMapFetcher, beforeAmendCabSearchQuoteFetcher, beforeAmendCarHireSearchfetcher, storageService, sendBackToQueueUpdater, basketCo2Fetcher, selectBasketMultiLevelApproversUpdater, validateBasketMiFetcher, validateBasketItemMiFetcher, setMIValuesUpdater, searchMIAutoSuggestValuesFetcher, getTrainSeatmapEvolviFetcher, reserveRailSeatsUpdater, evolviSeatmapsAreEnabledFetcher, getRequestedBookingUpdateFetcher, updateExchangeBasketUpdater, downloadETicketFetcher) {
|
|
14908
|
+
super(acceptNewPriceUpdater, addItemToBasketUpdater, addUserToBasketItem, basketCreator, basketFetcher, bookBasketUpdater, deleteBasketUpdater, helpers, removeItemFromBasketUpdater, revalidateBasketUpdater, getUserFetcher, getUserBasketsFetcher, setBasketItemLeadPassengerUpdater, moveItemToADifferentBasket, updateBasketNotes, updateBasketTitle, setBasketItemPaymentOption, getCancellationFetcher, userService, modalService, addGuestToBasketItem, addUpdatedItemToBasketUpdater, createBasketNote, getBasketNotes, getBasketApprovalTimestampsGQL, addBasketItemMarkupUpdater, addBasketItemCustomRemark, getBasketItemCustomRemarks, changeBasketOwnership, getBasketQuote, getProcessTermsPrice, acceptProcessTermsUpdater, resendApproverEmail, basketRequiresApproval, getOfficeApproversFetcher, selectBasketApproverUpdater, getDraftBasketsFetcher, createBasketFromDraftUpdater, selectBasketNotifyApproverUpdater, canAmendBookingFetcher, getUserApproversFetcher, getMIApproversFetcher, getBasketApprovalInfoFetcher, shareBasketUpdater, getUserSharedBasketsFetcher, getTrainSeatMapFetcher, beforeAmendCabSearchQuoteFetcher, beforeAmendCarHireSearchfetcher, storageService, sendBackToQueueUpdater, basketCo2Fetcher, selectBasketMultiLevelApproversUpdater, validateBasketMiFetcher, validateBasketItemMiFetcher, setMIValuesUpdater, searchMIAutoSuggestValuesFetcher, getTrainSeatmapEvolviFetcher, reserveRailSeatsUpdater, evolviSeatmapsAreEnabledFetcher, getRequestedBookingUpdateFetcher, updateExchangeBasketUpdater, downloadETicketFetcher);
|
|
14724
14909
|
}
|
|
14725
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseBasketService, deps: [{ token: AcceptNewPriceUpdater }, { token: AddItemToBasketUpdater }, { token: AddUserToBasketItemUpdater }, { token: CreateBasketUpdater }, { token: GetBasketFetcher }, { token: BookBasketUpdater }, { token: DeleteBasketUpdater }, { token: HelperRoutines }, { token: RemoveItemFromBasketUpdater }, { token: RevalidateBasketUpdater }, { token: GetUserFetcher }, { token: GetUserBasketsFetcher }, { token: SetBasketItemLeadPassengerUpdater }, { token: MoveBasketItemToBasketUpdater }, { token: UpdateBasketNotesUpdater }, { token: UpdateBasketTitleUpdater }, { token: UserService }, { token: ModalOpenerService }, { token: SetPaymentOptionUpdater }, { token: GetCancellationInfoFetcher }, { token: AddGuestToBasketItemUpdater }, { token: AddUpdatedItemToBasketUpdater }, { token: CreateBasketNoteUpdater }, { token: GetBasketNotesFetcher }, { token: GetBasketApprovalTimestampsFetcher }, { token: AddBasketItemMarkupUpdater }, { token: AddBasketItemCustomRemarkUpdater }, { token: GetBasketItemCustomRemarksFetcher }, { token: ChangeBasketOwnershipUpdater }, { token: GetBasketQuoteFetcher }, { token: ResendApproverEmailFetcher }, { token: CheckIfBasketRequiresApprovalFetcher }, { token: GetOfficeApproversFetcher }, { token: SelectBasketApproverUpdater }, { token: GetDraftBasketsFetcher }, { token: CreateBasketFromDraftUpdater }, { token: SelectBasketNotifyApproverUpdater }, { token: CanAmendBookingFetcher }, { token: GetUserApproversFetcher }, { token: GetMIApproversFetcher }, { token: GetBasketApprovalInfoFetcher }, { token: ShareBasketUpdater }, { token: GetUserSharedBasketsFetcher }, { token: GetTrainSeatmapFetcher }, { token: BeforeAmendCabSearchFetcher }, { token: BeforeAmendCarHireSearchFetcher }, { token: StorageService }, { token: SendBackToQueueUpdater }, { token: GetBasketCo2InfoFetcher }, { token: SelectBasketMultiLevelApproversUpdater }, { token: ValidateBasketMiFetcher }, { token: ValidateBasketItemMiFetcher }, { token: SetMIValuesUpdater }, { token: SearchMIAutoSuggestValuesFetcher }, { token: GetTrainSeatmapEvolviFetcher }, { token: ReserveRailSeatsUpdater }, { token: GetEvolviSeatmapsAreEnabledFetcher }, { token: GetRequestedBookingUpdateFetcher }, { token: UpdateExchangeBasketUpdater }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
14910
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseBasketService, deps: [{ token: AcceptNewPriceUpdater }, { token: AddItemToBasketUpdater }, { token: AddUserToBasketItemUpdater }, { token: CreateBasketUpdater }, { token: GetBasketFetcher }, { token: BookBasketUpdater }, { token: DeleteBasketUpdater }, { token: HelperRoutines }, { token: RemoveItemFromBasketUpdater }, { token: RevalidateBasketUpdater }, { token: GetUserFetcher }, { token: GetUserBasketsFetcher }, { token: SetBasketItemLeadPassengerUpdater }, { token: MoveBasketItemToBasketUpdater }, { token: UpdateBasketNotesUpdater }, { token: UpdateBasketTitleUpdater }, { token: UserService }, { token: ModalOpenerService }, { token: SetPaymentOptionUpdater }, { token: GetCancellationInfoFetcher }, { token: AddGuestToBasketItemUpdater }, { token: AddUpdatedItemToBasketUpdater }, { token: CreateBasketNoteUpdater }, { token: GetBasketNotesFetcher }, { token: GetBasketApprovalTimestampsFetcher }, { token: AddBasketItemMarkupUpdater }, { token: AddBasketItemCustomRemarkUpdater }, { token: GetBasketItemCustomRemarksFetcher }, { token: ChangeBasketOwnershipUpdater }, { token: GetBasketQuoteFetcher }, { token: GetProcessTermsPriceFetcher }, { token: AcceptProcessTermsUpdater }, { token: ResendApproverEmailFetcher }, { token: CheckIfBasketRequiresApprovalFetcher }, { token: GetOfficeApproversFetcher }, { token: SelectBasketApproverUpdater }, { token: GetDraftBasketsFetcher }, { token: CreateBasketFromDraftUpdater }, { token: SelectBasketNotifyApproverUpdater }, { token: CanAmendBookingFetcher }, { token: GetUserApproversFetcher }, { token: GetMIApproversFetcher }, { token: GetBasketApprovalInfoFetcher }, { token: ShareBasketUpdater }, { token: GetUserSharedBasketsFetcher }, { token: GetTrainSeatmapFetcher }, { token: BeforeAmendCabSearchFetcher }, { token: BeforeAmendCarHireSearchFetcher }, { token: StorageService }, { token: SendBackToQueueUpdater }, { token: GetBasketCo2InfoFetcher }, { token: SelectBasketMultiLevelApproversUpdater }, { token: ValidateBasketMiFetcher }, { token: ValidateBasketItemMiFetcher }, { token: SetMIValuesUpdater }, { token: SearchMIAutoSuggestValuesFetcher }, { token: GetTrainSeatmapEvolviFetcher }, { token: ReserveRailSeatsUpdater }, { token: GetEvolviSeatmapsAreEnabledFetcher }, { token: GetRequestedBookingUpdateFetcher }, { token: UpdateExchangeBasketUpdater }, { token: DownloadETicketFetcher }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
14726
14911
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseBasketService, providedIn: 'root' });
|
|
14727
14912
|
}
|
|
14728
14913
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseBasketService, decorators: [{
|
|
14729
14914
|
type: Injectable,
|
|
14730
14915
|
args: [{ providedIn: 'root' }]
|
|
14731
|
-
}], ctorParameters: () => [{ type: AcceptNewPriceUpdater }, { type: AddItemToBasketUpdater }, { type: AddUserToBasketItemUpdater }, { type: CreateBasketUpdater }, { type: GetBasketFetcher }, { type: BookBasketUpdater }, { type: DeleteBasketUpdater }, { type: HelperRoutines }, { type: RemoveItemFromBasketUpdater }, { type: RevalidateBasketUpdater }, { type: GetUserFetcher }, { type: GetUserBasketsFetcher }, { type: SetBasketItemLeadPassengerUpdater }, { type: MoveBasketItemToBasketUpdater }, { type: UpdateBasketNotesUpdater }, { type: UpdateBasketTitleUpdater }, { type: UserService }, { type: ModalOpenerService }, { type: SetPaymentOptionUpdater }, { type: GetCancellationInfoFetcher }, { type: AddGuestToBasketItemUpdater }, { type: AddUpdatedItemToBasketUpdater }, { type: CreateBasketNoteUpdater }, { type: GetBasketNotesFetcher }, { type: GetBasketApprovalTimestampsFetcher }, { type: AddBasketItemMarkupUpdater }, { type: AddBasketItemCustomRemarkUpdater }, { type: GetBasketItemCustomRemarksFetcher }, { type: ChangeBasketOwnershipUpdater }, { type: GetBasketQuoteFetcher }, { type: ResendApproverEmailFetcher }, { type: CheckIfBasketRequiresApprovalFetcher }, { type: GetOfficeApproversFetcher }, { type: SelectBasketApproverUpdater }, { type: GetDraftBasketsFetcher }, { type: CreateBasketFromDraftUpdater }, { type: SelectBasketNotifyApproverUpdater }, { type: CanAmendBookingFetcher }, { type: GetUserApproversFetcher }, { type: GetMIApproversFetcher }, { type: GetBasketApprovalInfoFetcher }, { type: ShareBasketUpdater }, { type: GetUserSharedBasketsFetcher }, { type: GetTrainSeatmapFetcher }, { type: BeforeAmendCabSearchFetcher }, { type: BeforeAmendCarHireSearchFetcher }, { type: StorageService }, { type: SendBackToQueueUpdater }, { type: GetBasketCo2InfoFetcher }, { type: SelectBasketMultiLevelApproversUpdater }, { type: ValidateBasketMiFetcher }, { type: ValidateBasketItemMiFetcher }, { type: SetMIValuesUpdater }, { type: SearchMIAutoSuggestValuesFetcher }, { type: GetTrainSeatmapEvolviFetcher }, { type: ReserveRailSeatsUpdater }, { type: GetEvolviSeatmapsAreEnabledFetcher }, { type: GetRequestedBookingUpdateFetcher }, { type: UpdateExchangeBasketUpdater }] });
|
|
14916
|
+
}], ctorParameters: () => [{ type: AcceptNewPriceUpdater }, { type: AddItemToBasketUpdater }, { type: AddUserToBasketItemUpdater }, { type: CreateBasketUpdater }, { type: GetBasketFetcher }, { type: BookBasketUpdater }, { type: DeleteBasketUpdater }, { type: HelperRoutines }, { type: RemoveItemFromBasketUpdater }, { type: RevalidateBasketUpdater }, { type: GetUserFetcher }, { type: GetUserBasketsFetcher }, { type: SetBasketItemLeadPassengerUpdater }, { type: MoveBasketItemToBasketUpdater }, { type: UpdateBasketNotesUpdater }, { type: UpdateBasketTitleUpdater }, { type: UserService }, { type: ModalOpenerService }, { type: SetPaymentOptionUpdater }, { type: GetCancellationInfoFetcher }, { type: AddGuestToBasketItemUpdater }, { type: AddUpdatedItemToBasketUpdater }, { type: CreateBasketNoteUpdater }, { type: GetBasketNotesFetcher }, { type: GetBasketApprovalTimestampsFetcher }, { type: AddBasketItemMarkupUpdater }, { type: AddBasketItemCustomRemarkUpdater }, { type: GetBasketItemCustomRemarksFetcher }, { type: ChangeBasketOwnershipUpdater }, { type: GetBasketQuoteFetcher }, { type: GetProcessTermsPriceFetcher }, { type: AcceptProcessTermsUpdater }, { type: ResendApproverEmailFetcher }, { type: CheckIfBasketRequiresApprovalFetcher }, { type: GetOfficeApproversFetcher }, { type: SelectBasketApproverUpdater }, { type: GetDraftBasketsFetcher }, { type: CreateBasketFromDraftUpdater }, { type: SelectBasketNotifyApproverUpdater }, { type: CanAmendBookingFetcher }, { type: GetUserApproversFetcher }, { type: GetMIApproversFetcher }, { type: GetBasketApprovalInfoFetcher }, { type: ShareBasketUpdater }, { type: GetUserSharedBasketsFetcher }, { type: GetTrainSeatmapFetcher }, { type: BeforeAmendCabSearchFetcher }, { type: BeforeAmendCarHireSearchFetcher }, { type: StorageService }, { type: SendBackToQueueUpdater }, { type: GetBasketCo2InfoFetcher }, { type: SelectBasketMultiLevelApproversUpdater }, { type: ValidateBasketMiFetcher }, { type: ValidateBasketItemMiFetcher }, { type: SetMIValuesUpdater }, { type: SearchMIAutoSuggestValuesFetcher }, { type: GetTrainSeatmapEvolviFetcher }, { type: ReserveRailSeatsUpdater }, { type: GetEvolviSeatmapsAreEnabledFetcher }, { type: GetRequestedBookingUpdateFetcher }, { type: UpdateExchangeBasketUpdater }, { type: DownloadETicketFetcher }] });
|
|
14732
14917
|
|
|
14733
14918
|
class HotelAvalibilityQuoteFetcher extends BaseHotelAvalibilityQuoteFetcher {
|
|
14734
14919
|
constructor(gql, helpers, modalService) {
|
|
@@ -16295,6 +16480,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
16295
16480
|
args: [{ providedIn: 'root' }]
|
|
16296
16481
|
}], ctorParameters: () => [{ type: SearchDiscoverLocationGQL }, { type: HelperRoutines }, { type: ModalOpenerService }] });
|
|
16297
16482
|
|
|
16483
|
+
class GetJyrneyUrlFetcher extends BaseGetJyrneyUrlFetcher {
|
|
16484
|
+
constructor(queryGQL, helpers, modalService) {
|
|
16485
|
+
super(queryGQL, helpers, modalService);
|
|
16486
|
+
}
|
|
16487
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetJyrneyUrlFetcher, deps: [{ token: GetJyrneyUrlGQL }, { token: HelperRoutines }, { token: ModalOpenerService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
16488
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetJyrneyUrlFetcher, providedIn: 'root' });
|
|
16489
|
+
}
|
|
16490
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GetJyrneyUrlFetcher, decorators: [{
|
|
16491
|
+
type: Injectable,
|
|
16492
|
+
args: [{ providedIn: 'root' }]
|
|
16493
|
+
}], ctorParameters: () => [{ type: GetJyrneyUrlGQL, decorators: [{
|
|
16494
|
+
type: Inject,
|
|
16495
|
+
args: [GetJyrneyUrlGQL]
|
|
16496
|
+
}] }, { type: HelperRoutines, decorators: [{
|
|
16497
|
+
type: Inject,
|
|
16498
|
+
args: [HelperRoutines]
|
|
16499
|
+
}] }, { type: ModalOpenerService }] });
|
|
16500
|
+
|
|
16298
16501
|
class GetPriceHiddenSabreExchangeFetcher extends BaseGetPriceHiddenSabreExchangeFetcher {
|
|
16299
16502
|
constructor(gql, modalService, helpers) {
|
|
16300
16503
|
super(gql, modalService, helpers);
|
|
@@ -16308,16 +16511,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
16308
16511
|
}], ctorParameters: () => [{ type: GetPriceHiddenSabreExchangeGQL }, { type: ModalOpenerService }, { type: HelperRoutines }] });
|
|
16309
16512
|
|
|
16310
16513
|
class EnterpriseSearchService extends BaseEnterpriseSearchService {
|
|
16311
|
-
constructor(searchAirportsFetcher, searchAirlinesFetcher, storageService, userService, router, helpers, hotelAvalibilityService, citiesFetcher, postcodeFetcher, railStationFetcher, saveUserAddressUpdater, editUserAddressUpdater, userAddressesFetcher, basketService, getUserCompanyOfficesFetcher, searchHotelChainsFetcher, getConfermaRoomImagesFetcher, isPostcodeValidValueTester, deleteUserAddressUpdater, deleteRecentSearchUpdater, saveRecentSearchUpdater, getUserCarbonAllowanceFetcher, saveFavouriteSearchUpdater, getHotelChainsFetcher, getRailProvidersFetcher, getAllAirlinesFetcher, getCarHireProvidersFetcher, environment, loungeFetcher, parkingFetcher, httpCallService, flightFetcher, hotelFetcher, carhireFetcher, carHireAvailabilityDetailFetcher, cabhireFetcher, railFetcher, irlFetcher, depotFetcher, getRailStationInfoFetcher, getRailStationFetcher, getIrlSupplierStationFetcher, webTokenService, riskAlertsFetcher, quicklistFetcher, getUserRecentSearchesFetcher, getRailCardsFetcher, getCovidMicrositeTokenFetcher, eurostarFetcher, searchUsersCanBookForFetcher, searchIrlStationsFetcher, irlDiscountCardFetcher, flightFareRulesFetcher, getFlightSeatMapFetcher, getRailLiveDeparturesFetcher, hotelRulesFetcher, flightRulesFetcher, getUserFavouriteSearchesFetcher, deleteFavouriteSearchUpdater, modalService, emailBasketFetcher, fasttrackFetcher, sendOfflineNotificationFetcher, generateBasketPdfFetcher, emailFerryFetcher, getFlightExtrasOptions, convertCurrencyFetcher, getCurrencyConversionRatesFetcher, getHotelDetailsFetcher, getServiceFetcher, getFlightBrandedFaresFetcher, getTrainlineSearchConfigFetcher, searchRailInwardFetcher, getRailSearchExchangeFetcher, createRailItineraryExchangeUpdater, getAirAvailbilityFetcher, getFlightAtNewClassFetcher, getFlightUpsellOffersFetcher, getOfficesFetcher, getFlightSearchExchangeFetcher, getFlightExchangeDetailsFetcher, emailApartmentBookingFetcher, emailSeasonTicketBookingFetcher, suggestPlacesFetcher, getLatLonFromHereIdFetcher, getGMTItineraryOptions, getGutCitySuggestionsFetcher, getGutLocationSuggestionsFetcher, GetRouteHappyFetcher, getMultipleHotelRatingFetcher, emailMeetingRoomBookingFetcher, emailAmbulanceBookingFetcher, getEntLocationByPostcode, getEntLocationByCity, getUserRecentBoltSearchesFetcher, saveRecentBoltSearchUpdater, getFerryPortsFetcher, getBannerFetcher, updateDOBUpdater, updateFlightSeatMapUpdater, deleteAllRecentSearchesUpdater, getOfficeDivisionsFetcher, searchGeoLocationFetcher, searchDiscoverLocationFetcher, getPriceHiddenSabreExchangeFetcher) {
|
|
16312
|
-
super(searchAirportsFetcher, searchAirlinesFetcher, storageService, userService, modalService, router, helpers, hotelAvalibilityService, citiesFetcher, postcodeFetcher, railStationFetcher, saveUserAddressUpdater, editUserAddressUpdater, userAddressesFetcher, basketService, getUserCompanyOfficesFetcher, searchHotelChainsFetcher, getConfermaRoomImagesFetcher, isPostcodeValidValueTester, depotFetcher, deleteUserAddressUpdater, deleteRecentSearchUpdater, saveRecentSearchUpdater, getUserCarbonAllowanceFetcher, saveFavouriteSearchUpdater, environment, loungeFetcher, parkingFetcher, httpCallService, flightFetcher, hotelFetcher, carhireFetcher, carHireAvailabilityDetailFetcher, cabhireFetcher, railFetcher, irlFetcher, getRailStationInfoFetcher, getRailStationFetcher, getIrlSupplierStationFetcher, webTokenService, riskAlertsFetcher, quicklistFetcher, getUserRecentSearchesFetcher, getRailCardsFetcher, getCovidMicrositeTokenFetcher, eurostarFetcher, searchUsersCanBookForFetcher, searchIrlStationsFetcher, irlDiscountCardFetcher, flightFareRulesFetcher, getFlightSeatMapFetcher, getRailLiveDeparturesFetcher, hotelRulesFetcher, flightRulesFetcher, getUserFavouriteSearchesFetcher, deleteFavouriteSearchUpdater, emailBasketFetcher, fasttrackFetcher, sendOfflineNotificationFetcher, getHotelChainsFetcher, getRailProvidersFetcher, getAllAirlinesFetcher, getCarHireProvidersFetcher, generateBasketPdfFetcher, emailFerryFetcher, getFlightExtrasOptions, convertCurrencyFetcher, getCurrencyConversionRatesFetcher, getHotelDetailsFetcher, getServiceFetcher, getFlightBrandedFaresFetcher, getTrainlineSearchConfigFetcher, searchRailInwardFetcher, getRailSearchExchangeFetcher, createRailItineraryExchangeUpdater, getAirAvailbilityFetcher, getFlightAtNewClassFetcher, getFlightUpsellOffersFetcher, getOfficesFetcher, emailApartmentBookingFetcher, emailSeasonTicketBookingFetcher, suggestPlacesFetcher, getLatLonFromHereIdFetcher, getGMTItineraryOptions, getGutCitySuggestionsFetcher, getGutLocationSuggestionsFetcher, GetRouteHappyFetcher, getMultipleHotelRatingFetcher, emailMeetingRoomBookingFetcher, getFlightSearchExchangeFetcher, getFlightExchangeDetailsFetcher, getEntLocationByPostcode, getEntLocationByCity, getUserRecentBoltSearchesFetcher, saveRecentBoltSearchUpdater, getFerryPortsFetcher, getBannerFetcher, updateDOBUpdater, updateFlightSeatMapUpdater, deleteAllRecentSearchesUpdater, getOfficeDivisionsFetcher, searchGeoLocationFetcher, searchDiscoverLocationFetcher, getPriceHiddenSabreExchangeFetcher, emailAmbulanceBookingFetcher);
|
|
16514
|
+
constructor(searchAirportsFetcher, searchAirlinesFetcher, storageService, userService, router, helpers, hotelAvalibilityService, citiesFetcher, postcodeFetcher, railStationFetcher, saveUserAddressUpdater, editUserAddressUpdater, userAddressesFetcher, basketService, getUserCompanyOfficesFetcher, searchHotelChainsFetcher, getConfermaRoomImagesFetcher, isPostcodeValidValueTester, deleteUserAddressUpdater, deleteRecentSearchUpdater, saveRecentSearchUpdater, getUserCarbonAllowanceFetcher, saveFavouriteSearchUpdater, getHotelChainsFetcher, getRailProvidersFetcher, getAllAirlinesFetcher, getCarHireProvidersFetcher, environment, loungeFetcher, parkingFetcher, httpCallService, flightFetcher, hotelFetcher, carhireFetcher, carHireAvailabilityDetailFetcher, cabhireFetcher, railFetcher, irlFetcher, depotFetcher, getRailStationInfoFetcher, getRailStationFetcher, getIrlSupplierStationFetcher, webTokenService, riskAlertsFetcher, quicklistFetcher, getUserRecentSearchesFetcher, getRailCardsFetcher, getCovidMicrositeTokenFetcher, eurostarFetcher, searchUsersCanBookForFetcher, searchIrlStationsFetcher, irlDiscountCardFetcher, flightFareRulesFetcher, getFlightSeatMapFetcher, getRailLiveDeparturesFetcher, hotelRulesFetcher, flightRulesFetcher, getUserFavouriteSearchesFetcher, deleteFavouriteSearchUpdater, modalService, emailBasketFetcher, fasttrackFetcher, sendOfflineNotificationFetcher, generateBasketPdfFetcher, emailFerryFetcher, getFlightExtrasOptions, convertCurrencyFetcher, getCurrencyConversionRatesFetcher, getHotelDetailsFetcher, getServiceFetcher, getFlightBrandedFaresFetcher, getTrainlineSearchConfigFetcher, searchRailInwardFetcher, getRailSearchExchangeFetcher, createRailItineraryExchangeUpdater, getAirAvailbilityFetcher, getFlightAtNewClassFetcher, getFlightUpsellOffersFetcher, getOfficesFetcher, getFlightSearchExchangeFetcher, getFlightExchangeDetailsFetcher, emailApartmentBookingFetcher, emailSeasonTicketBookingFetcher, suggestPlacesFetcher, getLatLonFromHereIdFetcher, getGMTItineraryOptions, getGutCitySuggestionsFetcher, getGutLocationSuggestionsFetcher, GetRouteHappyFetcher, getMultipleHotelRatingFetcher, emailMeetingRoomBookingFetcher, emailAmbulanceBookingFetcher, getEntLocationByPostcode, getEntLocationByCity, getUserRecentBoltSearchesFetcher, saveRecentBoltSearchUpdater, getFerryPortsFetcher, getBannerFetcher, updateDOBUpdater, updateFlightSeatMapUpdater, deleteAllRecentSearchesUpdater, getOfficeDivisionsFetcher, searchGeoLocationFetcher, searchDiscoverLocationFetcher, getJyrneyUrlFetcher, getPriceHiddenSabreExchangeFetcher) {
|
|
16515
|
+
super(searchAirportsFetcher, searchAirlinesFetcher, storageService, userService, modalService, router, helpers, hotelAvalibilityService, citiesFetcher, postcodeFetcher, railStationFetcher, saveUserAddressUpdater, editUserAddressUpdater, userAddressesFetcher, basketService, getUserCompanyOfficesFetcher, searchHotelChainsFetcher, getConfermaRoomImagesFetcher, isPostcodeValidValueTester, depotFetcher, deleteUserAddressUpdater, deleteRecentSearchUpdater, saveRecentSearchUpdater, getUserCarbonAllowanceFetcher, saveFavouriteSearchUpdater, environment, loungeFetcher, parkingFetcher, httpCallService, flightFetcher, hotelFetcher, carhireFetcher, carHireAvailabilityDetailFetcher, cabhireFetcher, railFetcher, irlFetcher, getRailStationInfoFetcher, getRailStationFetcher, getIrlSupplierStationFetcher, webTokenService, riskAlertsFetcher, quicklistFetcher, getUserRecentSearchesFetcher, getRailCardsFetcher, getCovidMicrositeTokenFetcher, eurostarFetcher, searchUsersCanBookForFetcher, searchIrlStationsFetcher, irlDiscountCardFetcher, flightFareRulesFetcher, getFlightSeatMapFetcher, getRailLiveDeparturesFetcher, hotelRulesFetcher, flightRulesFetcher, getUserFavouriteSearchesFetcher, deleteFavouriteSearchUpdater, emailBasketFetcher, fasttrackFetcher, sendOfflineNotificationFetcher, getHotelChainsFetcher, getRailProvidersFetcher, getAllAirlinesFetcher, getCarHireProvidersFetcher, generateBasketPdfFetcher, emailFerryFetcher, getFlightExtrasOptions, convertCurrencyFetcher, getCurrencyConversionRatesFetcher, getHotelDetailsFetcher, getServiceFetcher, getFlightBrandedFaresFetcher, getTrainlineSearchConfigFetcher, searchRailInwardFetcher, getRailSearchExchangeFetcher, createRailItineraryExchangeUpdater, getAirAvailbilityFetcher, getFlightAtNewClassFetcher, getFlightUpsellOffersFetcher, getOfficesFetcher, emailApartmentBookingFetcher, emailSeasonTicketBookingFetcher, suggestPlacesFetcher, getLatLonFromHereIdFetcher, getGMTItineraryOptions, getGutCitySuggestionsFetcher, getGutLocationSuggestionsFetcher, GetRouteHappyFetcher, getMultipleHotelRatingFetcher, emailMeetingRoomBookingFetcher, getFlightSearchExchangeFetcher, getFlightExchangeDetailsFetcher, getEntLocationByPostcode, getEntLocationByCity, getUserRecentBoltSearchesFetcher, saveRecentBoltSearchUpdater, getFerryPortsFetcher, getBannerFetcher, updateDOBUpdater, updateFlightSeatMapUpdater, deleteAllRecentSearchesUpdater, getOfficeDivisionsFetcher, searchGeoLocationFetcher, searchDiscoverLocationFetcher, getJyrneyUrlFetcher, getPriceHiddenSabreExchangeFetcher, emailAmbulanceBookingFetcher);
|
|
16313
16516
|
}
|
|
16314
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseSearchService, deps: [{ token: SearchAirportsFetcher }, { token: SearchAirlinesFetcher }, { token: StorageService }, { token: UserService }, { token: i2.Router }, { token: HelperRoutines }, { token: HotelAvalibilityService }, { token: SearchCityFetcher }, { token: SearchPostcodeFetcher }, { token: SearchRailStationsFetcher }, { token: SaveUserAddressUpdater }, { token: EditUserAddressUpdater }, { token: UserAddressesFetcher }, { token: EnterpriseBasketService }, { token: GetUserCompanyOfficesFetcher }, { token: SearchHotelChainsFetcher }, { token: GetConfermaRoomImagesFetcher }, { token: IsPostcodeValidValueTester }, { token: DeleteUserAddressUpdater }, { token: DeleteRecentSearchUpdater }, { token: SaveRecentSearchUpdater }, { token: GetUserCarbonAllowanceFetcher }, { token: SaveFavouriteSearchUpdater }, { token: GetHotelChainsFetcher }, { token: GetRailProvidersFetcher }, { token: GetAllAirlinesFetcher }, { token: GetCarHireProvidersFetcher }, { token: Environment }, { token: LoungeQuoteFetcher }, { token: ParkingQuoteFetcher }, { token: HttpCallService }, { token: FlightQuoteFetcher }, { token: HotelQuoteFetcher }, { token: CarhireQuoteFetcher }, { token: CarHireAvailabilityDetailFetcher }, { token: CabhireQuoteFetcher }, { token: RailQuoteFetcher }, { token: IrlQuoteFetcher }, { token: SearchDepotFetcher }, { token: GetRailStationInfoFetcher }, { token: GetRailStationFetcher }, { token: GetIrlSupplierStationFetcher }, { token: WebTokenService }, { token: GetRiskAlertsFetcher }, { token: SearchConfermaQuicklistFetcher }, { token: GetUserRecentSearchesFetcher }, { token: GetRailcardsFetcher }, { token: GetCovidMicrositeTokenFetcher }, { token: EurostarQuoteFetcher }, { token: SearchUsersCanBookForFetcher }, { token: SearchIrlStationsFetcher }, { token: IrlDiscountCardFetcher }, { token: GetFlightFareRulesFetcher }, { token: GetFlightSeatMapFetcher }, { token: GetRailLiveDeparturesFetcher }, { token: ApplyHotelRulesFetcher }, { token: ApplyJitFlightRulesFetcher }, { token: GetUserFavouriteSearchesFetcher }, { token: DeleteFavouriteSearchUpdater }, { token: ModalOpenerService }, { token: EmailBasketFetcher }, { token: FastTrackQuoteFetcher }, { token: SendOfflineNotificationFetcher }, { token: GenerateBasketPdfFetcher }, { token: EmailFerryBookingFetcher }, { token: GetFlightExtrasOptionsFetcher }, { token: ConvertCurrencyFetcher }, { token: GetCurrencyConversionRatesFetcher }, { token: GetHotelDetailsFetcher }, { token: GetServiceFetcher }, { token: GetFlightBrandedFaresFetcher }, { token: GetTrainlineSearchConfigFetcher }, { token: SearchRailInwardFetcher }, { token: GetRailSearchExchangeFetcher }, { token: CreateItineraryExchangeUpdater }, { token: GetAirAvailabilityFetcher }, { token: GetFlightAtNewClassFetcher }, { token: GetFlightUpsellOffersFetcher }, { token: GetOfficesFetcher }, { token: GetFlightSearchExchangeFetcher }, { token: GetFlightExchangeDetailsFetcher }, { token: EmailApartmentBookingFetcher }, { token: EmailSeasonTicketBookingFetcher }, { token: SuggestPlacesFetcher }, { token: GetLatLonFromHereIdFetcher }, { token: GetGmtItineraryOptionsFetcher }, { token: GetGutCitySuggestionsFetcher }, { token: GetGutLocationSuggestionsFetcher }, { token: GetRouteHappyFetcher }, { token: GetMultipleHotelRatingFetcher }, { token: EmailMeetingRoomBookingFetcher }, { token: EmailAmbulanceBookingFetcher }, { token: GetEntLocationByPostcode }, { token: GetEntLocationByCity }, { token: GetUserRecentBoltSearchesFetcher }, { token: SaveRecentBoltSearchUpdater }, { token: GetFerryPortsFetcher }, { token: GetBannerFetcher }, { token: UpdateDOBUpdater }, { token: UpdateFlightSeatMapUpdater }, { token: DeleteAllRecentSearchesUpdater }, { token: GetOfficeDivisionsFetcher }, { token: SearchGeoLocationFetcher }, { token: SearchDiscoverLocationFetcher }, { token: GetPriceHiddenSabreExchangeFetcher }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
16517
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseSearchService, deps: [{ token: SearchAirportsFetcher }, { token: SearchAirlinesFetcher }, { token: StorageService }, { token: UserService }, { token: i2.Router }, { token: HelperRoutines }, { token: HotelAvalibilityService }, { token: SearchCityFetcher }, { token: SearchPostcodeFetcher }, { token: SearchRailStationsFetcher }, { token: SaveUserAddressUpdater }, { token: EditUserAddressUpdater }, { token: UserAddressesFetcher }, { token: EnterpriseBasketService }, { token: GetUserCompanyOfficesFetcher }, { token: SearchHotelChainsFetcher }, { token: GetConfermaRoomImagesFetcher }, { token: IsPostcodeValidValueTester }, { token: DeleteUserAddressUpdater }, { token: DeleteRecentSearchUpdater }, { token: SaveRecentSearchUpdater }, { token: GetUserCarbonAllowanceFetcher }, { token: SaveFavouriteSearchUpdater }, { token: GetHotelChainsFetcher }, { token: GetRailProvidersFetcher }, { token: GetAllAirlinesFetcher }, { token: GetCarHireProvidersFetcher }, { token: Environment }, { token: LoungeQuoteFetcher }, { token: ParkingQuoteFetcher }, { token: HttpCallService }, { token: FlightQuoteFetcher }, { token: HotelQuoteFetcher }, { token: CarhireQuoteFetcher }, { token: CarHireAvailabilityDetailFetcher }, { token: CabhireQuoteFetcher }, { token: RailQuoteFetcher }, { token: IrlQuoteFetcher }, { token: SearchDepotFetcher }, { token: GetRailStationInfoFetcher }, { token: GetRailStationFetcher }, { token: GetIrlSupplierStationFetcher }, { token: WebTokenService }, { token: GetRiskAlertsFetcher }, { token: SearchConfermaQuicklistFetcher }, { token: GetUserRecentSearchesFetcher }, { token: GetRailcardsFetcher }, { token: GetCovidMicrositeTokenFetcher }, { token: EurostarQuoteFetcher }, { token: SearchUsersCanBookForFetcher }, { token: SearchIrlStationsFetcher }, { token: IrlDiscountCardFetcher }, { token: GetFlightFareRulesFetcher }, { token: GetFlightSeatMapFetcher }, { token: GetRailLiveDeparturesFetcher }, { token: ApplyHotelRulesFetcher }, { token: ApplyJitFlightRulesFetcher }, { token: GetUserFavouriteSearchesFetcher }, { token: DeleteFavouriteSearchUpdater }, { token: ModalOpenerService }, { token: EmailBasketFetcher }, { token: FastTrackQuoteFetcher }, { token: SendOfflineNotificationFetcher }, { token: GenerateBasketPdfFetcher }, { token: EmailFerryBookingFetcher }, { token: GetFlightExtrasOptionsFetcher }, { token: ConvertCurrencyFetcher }, { token: GetCurrencyConversionRatesFetcher }, { token: GetHotelDetailsFetcher }, { token: GetServiceFetcher }, { token: GetFlightBrandedFaresFetcher }, { token: GetTrainlineSearchConfigFetcher }, { token: SearchRailInwardFetcher }, { token: GetRailSearchExchangeFetcher }, { token: CreateItineraryExchangeUpdater }, { token: GetAirAvailabilityFetcher }, { token: GetFlightAtNewClassFetcher }, { token: GetFlightUpsellOffersFetcher }, { token: GetOfficesFetcher }, { token: GetFlightSearchExchangeFetcher }, { token: GetFlightExchangeDetailsFetcher }, { token: EmailApartmentBookingFetcher }, { token: EmailSeasonTicketBookingFetcher }, { token: SuggestPlacesFetcher }, { token: GetLatLonFromHereIdFetcher }, { token: GetGmtItineraryOptionsFetcher }, { token: GetGutCitySuggestionsFetcher }, { token: GetGutLocationSuggestionsFetcher }, { token: GetRouteHappyFetcher }, { token: GetMultipleHotelRatingFetcher }, { token: EmailMeetingRoomBookingFetcher }, { token: EmailAmbulanceBookingFetcher }, { token: GetEntLocationByPostcode }, { token: GetEntLocationByCity }, { token: GetUserRecentBoltSearchesFetcher }, { token: SaveRecentBoltSearchUpdater }, { token: GetFerryPortsFetcher }, { token: GetBannerFetcher }, { token: UpdateDOBUpdater }, { token: UpdateFlightSeatMapUpdater }, { token: DeleteAllRecentSearchesUpdater }, { token: GetOfficeDivisionsFetcher }, { token: SearchGeoLocationFetcher }, { token: SearchDiscoverLocationFetcher }, { token: GetJyrneyUrlFetcher }, { token: GetPriceHiddenSabreExchangeFetcher }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
16315
16518
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseSearchService, providedIn: 'root' });
|
|
16316
16519
|
}
|
|
16317
16520
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: EnterpriseSearchService, decorators: [{
|
|
16318
16521
|
type: Injectable,
|
|
16319
16522
|
args: [{ providedIn: 'root' }]
|
|
16320
|
-
}], ctorParameters: () => [{ type: SearchAirportsFetcher }, { type: SearchAirlinesFetcher }, { type: StorageService }, { type: UserService }, { type: i2.Router }, { type: HelperRoutines }, { type: HotelAvalibilityService }, { type: SearchCityFetcher }, { type: SearchPostcodeFetcher }, { type: SearchRailStationsFetcher }, { type: SaveUserAddressUpdater }, { type: EditUserAddressUpdater }, { type: UserAddressesFetcher }, { type: EnterpriseBasketService }, { type: GetUserCompanyOfficesFetcher }, { type: SearchHotelChainsFetcher }, { type: GetConfermaRoomImagesFetcher }, { type: IsPostcodeValidValueTester }, { type: DeleteUserAddressUpdater }, { type: DeleteRecentSearchUpdater }, { type: SaveRecentSearchUpdater }, { type: GetUserCarbonAllowanceFetcher }, { type: SaveFavouriteSearchUpdater }, { type: GetHotelChainsFetcher }, { type: GetRailProvidersFetcher }, { type: GetAllAirlinesFetcher }, { type: GetCarHireProvidersFetcher }, { type: Environment }, { type: LoungeQuoteFetcher }, { type: ParkingQuoteFetcher }, { type: HttpCallService }, { type: FlightQuoteFetcher }, { type: HotelQuoteFetcher }, { type: CarhireQuoteFetcher }, { type: CarHireAvailabilityDetailFetcher }, { type: CabhireQuoteFetcher }, { type: RailQuoteFetcher }, { type: IrlQuoteFetcher }, { type: SearchDepotFetcher }, { type: GetRailStationInfoFetcher }, { type: GetRailStationFetcher }, { type: GetIrlSupplierStationFetcher }, { type: WebTokenService }, { type: GetRiskAlertsFetcher }, { type: SearchConfermaQuicklistFetcher }, { type: GetUserRecentSearchesFetcher }, { type: GetRailcardsFetcher }, { type: GetCovidMicrositeTokenFetcher }, { type: EurostarQuoteFetcher }, { type: SearchUsersCanBookForFetcher }, { type: SearchIrlStationsFetcher }, { type: IrlDiscountCardFetcher }, { type: GetFlightFareRulesFetcher }, { type: GetFlightSeatMapFetcher }, { type: GetRailLiveDeparturesFetcher }, { type: ApplyHotelRulesFetcher }, { type: ApplyJitFlightRulesFetcher }, { type: GetUserFavouriteSearchesFetcher }, { type: DeleteFavouriteSearchUpdater }, { type: ModalOpenerService }, { type: EmailBasketFetcher }, { type: FastTrackQuoteFetcher }, { type: SendOfflineNotificationFetcher }, { type: GenerateBasketPdfFetcher }, { type: EmailFerryBookingFetcher }, { type: GetFlightExtrasOptionsFetcher }, { type: ConvertCurrencyFetcher }, { type: GetCurrencyConversionRatesFetcher }, { type: GetHotelDetailsFetcher }, { type: GetServiceFetcher }, { type: GetFlightBrandedFaresFetcher }, { type: GetTrainlineSearchConfigFetcher }, { type: SearchRailInwardFetcher }, { type: GetRailSearchExchangeFetcher }, { type: CreateItineraryExchangeUpdater }, { type: GetAirAvailabilityFetcher }, { type: GetFlightAtNewClassFetcher }, { type: GetFlightUpsellOffersFetcher }, { type: GetOfficesFetcher }, { type: GetFlightSearchExchangeFetcher }, { type: GetFlightExchangeDetailsFetcher }, { type: EmailApartmentBookingFetcher }, { type: EmailSeasonTicketBookingFetcher }, { type: SuggestPlacesFetcher }, { type: GetLatLonFromHereIdFetcher }, { type: GetGmtItineraryOptionsFetcher }, { type: GetGutCitySuggestionsFetcher }, { type: GetGutLocationSuggestionsFetcher }, { type: GetRouteHappyFetcher }, { type: GetMultipleHotelRatingFetcher }, { type: EmailMeetingRoomBookingFetcher }, { type: EmailAmbulanceBookingFetcher }, { type: GetEntLocationByPostcode }, { type: GetEntLocationByCity }, { type: GetUserRecentBoltSearchesFetcher }, { type: SaveRecentBoltSearchUpdater }, { type: GetFerryPortsFetcher }, { type: GetBannerFetcher }, { type: UpdateDOBUpdater }, { type: UpdateFlightSeatMapUpdater }, { type: DeleteAllRecentSearchesUpdater }, { type: GetOfficeDivisionsFetcher }, { type: SearchGeoLocationFetcher }, { type: SearchDiscoverLocationFetcher }, { type: GetPriceHiddenSabreExchangeFetcher }] });
|
|
16523
|
+
}], ctorParameters: () => [{ type: SearchAirportsFetcher }, { type: SearchAirlinesFetcher }, { type: StorageService }, { type: UserService }, { type: i2.Router }, { type: HelperRoutines }, { type: HotelAvalibilityService }, { type: SearchCityFetcher }, { type: SearchPostcodeFetcher }, { type: SearchRailStationsFetcher }, { type: SaveUserAddressUpdater }, { type: EditUserAddressUpdater }, { type: UserAddressesFetcher }, { type: EnterpriseBasketService }, { type: GetUserCompanyOfficesFetcher }, { type: SearchHotelChainsFetcher }, { type: GetConfermaRoomImagesFetcher }, { type: IsPostcodeValidValueTester }, { type: DeleteUserAddressUpdater }, { type: DeleteRecentSearchUpdater }, { type: SaveRecentSearchUpdater }, { type: GetUserCarbonAllowanceFetcher }, { type: SaveFavouriteSearchUpdater }, { type: GetHotelChainsFetcher }, { type: GetRailProvidersFetcher }, { type: GetAllAirlinesFetcher }, { type: GetCarHireProvidersFetcher }, { type: Environment }, { type: LoungeQuoteFetcher }, { type: ParkingQuoteFetcher }, { type: HttpCallService }, { type: FlightQuoteFetcher }, { type: HotelQuoteFetcher }, { type: CarhireQuoteFetcher }, { type: CarHireAvailabilityDetailFetcher }, { type: CabhireQuoteFetcher }, { type: RailQuoteFetcher }, { type: IrlQuoteFetcher }, { type: SearchDepotFetcher }, { type: GetRailStationInfoFetcher }, { type: GetRailStationFetcher }, { type: GetIrlSupplierStationFetcher }, { type: WebTokenService }, { type: GetRiskAlertsFetcher }, { type: SearchConfermaQuicklistFetcher }, { type: GetUserRecentSearchesFetcher }, { type: GetRailcardsFetcher }, { type: GetCovidMicrositeTokenFetcher }, { type: EurostarQuoteFetcher }, { type: SearchUsersCanBookForFetcher }, { type: SearchIrlStationsFetcher }, { type: IrlDiscountCardFetcher }, { type: GetFlightFareRulesFetcher }, { type: GetFlightSeatMapFetcher }, { type: GetRailLiveDeparturesFetcher }, { type: ApplyHotelRulesFetcher }, { type: ApplyJitFlightRulesFetcher }, { type: GetUserFavouriteSearchesFetcher }, { type: DeleteFavouriteSearchUpdater }, { type: ModalOpenerService }, { type: EmailBasketFetcher }, { type: FastTrackQuoteFetcher }, { type: SendOfflineNotificationFetcher }, { type: GenerateBasketPdfFetcher }, { type: EmailFerryBookingFetcher }, { type: GetFlightExtrasOptionsFetcher }, { type: ConvertCurrencyFetcher }, { type: GetCurrencyConversionRatesFetcher }, { type: GetHotelDetailsFetcher }, { type: GetServiceFetcher }, { type: GetFlightBrandedFaresFetcher }, { type: GetTrainlineSearchConfigFetcher }, { type: SearchRailInwardFetcher }, { type: GetRailSearchExchangeFetcher }, { type: CreateItineraryExchangeUpdater }, { type: GetAirAvailabilityFetcher }, { type: GetFlightAtNewClassFetcher }, { type: GetFlightUpsellOffersFetcher }, { type: GetOfficesFetcher }, { type: GetFlightSearchExchangeFetcher }, { type: GetFlightExchangeDetailsFetcher }, { type: EmailApartmentBookingFetcher }, { type: EmailSeasonTicketBookingFetcher }, { type: SuggestPlacesFetcher }, { type: GetLatLonFromHereIdFetcher }, { type: GetGmtItineraryOptionsFetcher }, { type: GetGutCitySuggestionsFetcher }, { type: GetGutLocationSuggestionsFetcher }, { type: GetRouteHappyFetcher }, { type: GetMultipleHotelRatingFetcher }, { type: EmailMeetingRoomBookingFetcher }, { type: EmailAmbulanceBookingFetcher }, { type: GetEntLocationByPostcode }, { type: GetEntLocationByCity }, { type: GetUserRecentBoltSearchesFetcher }, { type: SaveRecentBoltSearchUpdater }, { type: GetFerryPortsFetcher }, { type: GetBannerFetcher }, { type: UpdateDOBUpdater }, { type: UpdateFlightSeatMapUpdater }, { type: DeleteAllRecentSearchesUpdater }, { type: GetOfficeDivisionsFetcher }, { type: SearchGeoLocationFetcher }, { type: SearchDiscoverLocationFetcher }, { type: GetJyrneyUrlFetcher }, { type: GetPriceHiddenSabreExchangeFetcher }] });
|
|
16321
16524
|
|
|
16322
16525
|
class GetLatestVersionsFetcher extends BaseGetLatestVersionsFetcher {
|
|
16323
16526
|
constructor(queryGQL, helpers, modalService) {
|
|
@@ -16598,9 +16801,9 @@ class RailFareProcessingService {
|
|
|
16598
16801
|
const hasAdvance = fares.some((fare) => fare?.name?.toLowerCase().includes(RailFareNameKeywords.ADVANCE));
|
|
16599
16802
|
const hasFlexible = fares.some((fare) => {
|
|
16600
16803
|
const nameLower = fare?.name?.toLowerCase() || '';
|
|
16601
|
-
return nameLower.includes(RailFareNameKeywords.OFF_PEAK) ||
|
|
16804
|
+
return (nameLower.includes(RailFareNameKeywords.OFF_PEAK) ||
|
|
16602
16805
|
nameLower.includes(RailFareNameKeywords.ANYTIME) ||
|
|
16603
|
-
nameLower.includes(RailFareNameKeywords.SUPER_OFF_PEAK);
|
|
16806
|
+
nameLower.includes(RailFareNameKeywords.SUPER_OFF_PEAK));
|
|
16604
16807
|
});
|
|
16605
16808
|
const hasPremiumOrStandard = fares.some((fare) => {
|
|
16606
16809
|
const nameLower = fare?.name?.toLowerCase() || '';
|
|
@@ -16633,7 +16836,8 @@ class RailFareProcessingService {
|
|
|
16633
16836
|
let cheapestTicket = null;
|
|
16634
16837
|
let cheapestPrice = Number.MAX_SAFE_INTEGER;
|
|
16635
16838
|
for (const candidateTicket of allFares) {
|
|
16636
|
-
if (!candidateTicket ||
|
|
16839
|
+
if (!candidateTicket ||
|
|
16840
|
+
candidateTicket.identifiers?.isSplit ||
|
|
16637
16841
|
candidateTicket.class !== splitTicket.class ||
|
|
16638
16842
|
candidateTicket.singleOrReturn !== splitTicket.singleOrReturn) {
|
|
16639
16843
|
continue;
|
|
@@ -16657,15 +16861,15 @@ class RailFareProcessingService {
|
|
|
16657
16861
|
return candidateTypeLower.includes('advance');
|
|
16658
16862
|
}
|
|
16659
16863
|
else if (splitType === RailSplitType.FLEXIBLE) {
|
|
16660
|
-
return candidateTypeLower.includes('off-peak') ||
|
|
16864
|
+
return (candidateTypeLower.includes('off-peak') ||
|
|
16661
16865
|
candidateTypeLower.includes('anytime') ||
|
|
16662
|
-
candidateTypeLower.includes('super off-peak');
|
|
16866
|
+
candidateTypeLower.includes('super off-peak'));
|
|
16663
16867
|
}
|
|
16664
16868
|
else if (splitType === RailSplitType.COMBINED) {
|
|
16665
|
-
return candidateTypeLower.includes('advance') ||
|
|
16869
|
+
return (candidateTypeLower.includes('advance') ||
|
|
16666
16870
|
candidateTypeLower.includes('off-peak') ||
|
|
16667
16871
|
candidateTypeLower.includes('anytime') ||
|
|
16668
|
-
candidateTypeLower.includes('super off-peak');
|
|
16872
|
+
candidateTypeLower.includes('super off-peak'));
|
|
16669
16873
|
}
|
|
16670
16874
|
return true;
|
|
16671
16875
|
}
|
|
@@ -16701,7 +16905,7 @@ class RailFareProcessingService {
|
|
|
16701
16905
|
* Find the journey that contains a specific ticket
|
|
16702
16906
|
*/
|
|
16703
16907
|
findJourneyContainingTicket(targetTicket, journeys) {
|
|
16704
|
-
return journeys.find(journey => {
|
|
16908
|
+
return (journeys.find((journey) => {
|
|
16705
16909
|
const allFares = [
|
|
16706
16910
|
...Object.values(journey.singleJourneyFares || {}).flat(),
|
|
16707
16911
|
...Object.values(journey.returnJourneyFares || {}).flat(),
|
|
@@ -16709,8 +16913,8 @@ class RailFareProcessingService {
|
|
|
16709
16913
|
...Object.values(journey.openReturnJourneyFares || {}).flat(),
|
|
16710
16914
|
...Object.values(journey.splitFares || {}).flat()
|
|
16711
16915
|
];
|
|
16712
|
-
return allFares.some(ticket => ticket?.fareHash === targetTicket.fareHash);
|
|
16713
|
-
}) || null;
|
|
16916
|
+
return allFares.some((ticket) => ticket?.fareHash === targetTicket.fareHash);
|
|
16917
|
+
}) || null);
|
|
16714
16918
|
}
|
|
16715
16919
|
/**
|
|
16716
16920
|
* Find the cheapest non-split ticket in a journey
|
|
@@ -16721,7 +16925,7 @@ class RailFareProcessingService {
|
|
|
16721
16925
|
...Object.values(journey.returnJourneyFares || {}).flat(),
|
|
16722
16926
|
...Object.values(journey.dualSingleJourneyFares || {}).flat(),
|
|
16723
16927
|
...Object.values(journey.openReturnJourneyFares || {}).flat()
|
|
16724
|
-
].filter(ticket => ticket && !ticket.identifiers?.isSplit && ticket.selectable && !ticket.unavailable);
|
|
16928
|
+
].filter((ticket) => ticket && !ticket.identifiers?.isSplit && ticket.selectable && !ticket.unavailable);
|
|
16725
16929
|
if (!nonSplitFares.length) {
|
|
16726
16930
|
return null;
|
|
16727
16931
|
}
|
|
@@ -16743,10 +16947,10 @@ class RailFareProcessingService {
|
|
|
16743
16947
|
journey.openReturnJourneyFares,
|
|
16744
16948
|
journey.splitFares
|
|
16745
16949
|
];
|
|
16746
|
-
return fareTypes.some(fareType => {
|
|
16950
|
+
return fareTypes.some((fareType) => {
|
|
16747
16951
|
if (!fareType)
|
|
16748
16952
|
return false;
|
|
16749
|
-
return Object.values(fareType).some((fareArray) => fareArray?.some(fare => fare?.fareHash === ticket.fareHash));
|
|
16953
|
+
return Object.values(fareType).some((fareArray) => fareArray?.some((fare) => fare?.fareHash === ticket.fareHash));
|
|
16750
16954
|
});
|
|
16751
16955
|
}
|
|
16752
16956
|
/**
|
|
@@ -16761,12 +16965,11 @@ class RailFareProcessingService {
|
|
|
16761
16965
|
journey.openReturnJourneyFares,
|
|
16762
16966
|
journey.splitFares
|
|
16763
16967
|
];
|
|
16764
|
-
fareTypes.forEach(fareType => {
|
|
16968
|
+
fareTypes.forEach((fareType) => {
|
|
16765
16969
|
if (fareType) {
|
|
16766
16970
|
Object.values(fareType).forEach((fareArray) => {
|
|
16767
|
-
fareArray?.forEach(fare => {
|
|
16768
|
-
if (fare?.class === ticketClass &&
|
|
16769
|
-
(fare?.singleOrReturn === singleOrReturn)) {
|
|
16971
|
+
fareArray?.forEach((fare) => {
|
|
16972
|
+
if (fare?.class === ticketClass && fare?.singleOrReturn === singleOrReturn) {
|
|
16770
16973
|
allTickets.push(fare);
|
|
16771
16974
|
}
|
|
16772
16975
|
});
|
|
@@ -16774,7 +16977,7 @@ class RailFareProcessingService {
|
|
|
16774
16977
|
}
|
|
16775
16978
|
});
|
|
16776
16979
|
// Remove duplicates based on fareHash
|
|
16777
|
-
const uniqueTickets = allTickets.filter((ticket, index, self) => index === self.findIndex(t => t.fareHash === ticket.fareHash));
|
|
16980
|
+
const uniqueTickets = allTickets.filter((ticket, index, self) => index === self.findIndex((t) => t.fareHash === ticket.fareHash));
|
|
16778
16981
|
// Sort by price ascending
|
|
16779
16982
|
return uniqueTickets.sort((a, b) => Number(a.price) - Number(b.price));
|
|
16780
16983
|
}
|
|
@@ -16796,7 +16999,11 @@ class RailFareProcessingService {
|
|
|
16796
16999
|
else if (splitType === RailSplitType.COMBINED) {
|
|
16797
17000
|
comparisonType = 'cheapest single fare';
|
|
16798
17001
|
}
|
|
16799
|
-
return baseTooltip +
|
|
17002
|
+
return (baseTooltip +
|
|
17003
|
+
', saving ' +
|
|
17004
|
+
this.formatCurrency(+savings, ticket.currency) +
|
|
17005
|
+
' compared to booking the ' +
|
|
17006
|
+
comparisonType);
|
|
16800
17007
|
}
|
|
16801
17008
|
return baseTooltip;
|
|
16802
17009
|
}
|
|
@@ -16820,7 +17027,7 @@ class RailFareProcessingService {
|
|
|
16820
17027
|
return true;
|
|
16821
17028
|
}
|
|
16822
17029
|
}
|
|
16823
|
-
else if (ticket?.discountInfo?.some(x => x.type === DiscountType.Groupsave)) {
|
|
17030
|
+
else if (ticket?.discountInfo?.some((x) => x.type === DiscountType.Groupsave)) {
|
|
16824
17031
|
return true;
|
|
16825
17032
|
}
|
|
16826
17033
|
return false;
|
|
@@ -16835,8 +17042,8 @@ class RailFareProcessingService {
|
|
|
16835
17042
|
* Check if split tooltip should be shown
|
|
16836
17043
|
*/
|
|
16837
17044
|
shouldShowSplitTooltip(ticket) {
|
|
16838
|
-
return ticket?.identifiers?.isSplit &&
|
|
16839
|
-
ticket?.identifiers?.composition?.includes(RailSearchComposition.InterchangeSplit);
|
|
17045
|
+
return (ticket?.identifiers?.isSplit &&
|
|
17046
|
+
ticket?.identifiers?.composition?.includes(RailSearchComposition.InterchangeSplit));
|
|
16840
17047
|
}
|
|
16841
17048
|
/**
|
|
16842
17049
|
* Get the fare type for a ticket within a journey
|
|
@@ -16866,19 +17073,19 @@ class RailFareProcessingService {
|
|
|
16866
17073
|
*/
|
|
16867
17074
|
calculateTicketSavings(journeys) {
|
|
16868
17075
|
const ticketSavings = {};
|
|
16869
|
-
journeys.forEach(journey => {
|
|
17076
|
+
journeys.forEach((journey) => {
|
|
16870
17077
|
const fareTypes = [
|
|
16871
17078
|
journey.dualSingleJourneyFares,
|
|
16872
17079
|
journey.returnJourneyFares,
|
|
16873
17080
|
journey.splitFares,
|
|
16874
17081
|
journey.singleJourneyFares
|
|
16875
17082
|
];
|
|
16876
|
-
fareTypes.forEach(fares => {
|
|
17083
|
+
fareTypes.forEach((fares) => {
|
|
16877
17084
|
if (fares) {
|
|
16878
|
-
Object.keys(fares).forEach(className => {
|
|
17085
|
+
Object.keys(fares).forEach((className) => {
|
|
16879
17086
|
const tickets = fares[className];
|
|
16880
17087
|
if (Array.isArray(tickets)) {
|
|
16881
|
-
tickets.forEach(ticket => {
|
|
17088
|
+
tickets.forEach((ticket) => {
|
|
16882
17089
|
if (ticket?.fareHash) {
|
|
16883
17090
|
ticketSavings[ticket.fareHash] = this.splitSavings(ticket, journeys);
|
|
16884
17091
|
}
|
|
@@ -16945,16 +17152,23 @@ class RailResultsStateService {
|
|
|
16945
17152
|
resultsObject.standardReturns[journey.journeyHash] = standardReturns[0];
|
|
16946
17153
|
resultsObject.firstClassReturns[journey.journeyHash] = firstClassReturns[0];
|
|
16947
17154
|
// Calculate date difference from first journey
|
|
16948
|
-
const diff = moment$1(journey.departDateTime)
|
|
17155
|
+
const diff = moment$1(journey.departDateTime)
|
|
17156
|
+
.startOf('day')
|
|
17157
|
+
.diff(moment$1(journeys[0].departDateTime).startOf('day'), 'day');
|
|
16949
17158
|
datesAfterInitial.push(!!diff);
|
|
16950
17159
|
// Check for busy times
|
|
16951
17160
|
const departDateTime = moment$1(journey.departDateTime);
|
|
16952
|
-
const departTimeToday = moment$1()
|
|
17161
|
+
const departTimeToday = moment$1()
|
|
17162
|
+
.clone()
|
|
17163
|
+
.hour(departDateTime.hour())
|
|
17164
|
+
.minute(departDateTime.minute())
|
|
17165
|
+
.second(departDateTime.second());
|
|
16953
17166
|
const sixThirty = moment$1('06:30', 'HH:mm');
|
|
16954
17167
|
const eightThirty = moment$1('08:30', 'HH:mm');
|
|
16955
17168
|
const sixteenThirty = moment$1('16:30', 'HH:mm');
|
|
16956
17169
|
const eighteen = moment$1('18:00', 'HH:mm');
|
|
16957
|
-
if (departTimeToday.isBetween(sixThirty, eightThirty) ||
|
|
17170
|
+
if (departTimeToday.isBetween(sixThirty, eightThirty) ||
|
|
17171
|
+
departTimeToday.isBetween(sixteenThirty, eighteen)) {
|
|
16958
17172
|
busyTimes[journey.journeyHash] = true;
|
|
16959
17173
|
}
|
|
16960
17174
|
});
|
|
@@ -16973,7 +17187,7 @@ class RailResultsStateService {
|
|
|
16973
17187
|
journey.singleJourneyFares,
|
|
16974
17188
|
journey.splitFares
|
|
16975
17189
|
];
|
|
16976
|
-
fareTypes.forEach(fareType => {
|
|
17190
|
+
fareTypes.forEach((fareType) => {
|
|
16977
17191
|
if (fareType) {
|
|
16978
17192
|
Object.keys(fareType).forEach((key) => {
|
|
16979
17193
|
const arrayOfTickets = fareType[key];
|
|
@@ -17012,15 +17226,19 @@ class RailResultsStateService {
|
|
|
17012
17226
|
journey.singleJourneyFares,
|
|
17013
17227
|
journey.splitFares
|
|
17014
17228
|
];
|
|
17015
|
-
fareTypes.forEach(fareType => {
|
|
17229
|
+
fareTypes.forEach((fareType) => {
|
|
17016
17230
|
if (fareType) {
|
|
17017
17231
|
Object.keys(fareType).forEach((key) => {
|
|
17018
17232
|
const arrayOfTickets = fareType[key];
|
|
17019
17233
|
arrayOfTickets?.forEach((ticket) => {
|
|
17020
|
-
if (ticket?.class === RailClass.Standard &&
|
|
17234
|
+
if (ticket?.class === RailClass.Standard &&
|
|
17235
|
+
ticket?.singleOrReturn === SingleOrReturn.Return &&
|
|
17236
|
+
ticket?.selectable) {
|
|
17021
17237
|
standardReturns.push(ticket);
|
|
17022
17238
|
}
|
|
17023
|
-
else if (ticket?.class === RailClass.First &&
|
|
17239
|
+
else if (ticket?.class === RailClass.First &&
|
|
17240
|
+
ticket?.singleOrReturn === SingleOrReturn.Return &&
|
|
17241
|
+
ticket?.selectable) {
|
|
17024
17242
|
firstClassReturns.push(ticket);
|
|
17025
17243
|
}
|
|
17026
17244
|
});
|
|
@@ -17099,7 +17317,7 @@ class RailResultsStateService {
|
|
|
17099
17317
|
* Sort fares by price ascending
|
|
17100
17318
|
*/
|
|
17101
17319
|
sortByPrice(fares) {
|
|
17102
|
-
return fares.sort((a, b) => (a.price > b.price
|
|
17320
|
+
return fares.sort((a, b) => (a.price > b.price ? 1 : b.price > a.price ? -1 : 0));
|
|
17103
17321
|
}
|
|
17104
17322
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: RailResultsStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
17105
17323
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: RailResultsStateService, providedIn: 'root' });
|
|
@@ -17238,8 +17456,10 @@ class SearchPaymentValidationService {
|
|
|
17238
17456
|
*/
|
|
17239
17457
|
validatePaymentMethods(services) {
|
|
17240
17458
|
return this.userService.getApexxListCards().pipe(map((cards) => {
|
|
17241
|
-
const hasNoStoredCard = !cards?.some(card => card.status === 'ENABLED');
|
|
17242
|
-
const serviceList = Object.values(services)
|
|
17459
|
+
const hasNoStoredCard = !cards?.some((card) => card.status === 'ENABLED');
|
|
17460
|
+
const serviceList = Object.values(services)
|
|
17461
|
+
.flat()
|
|
17462
|
+
.filter((service) => service.type !== ServiceType.Ferry);
|
|
17243
17463
|
const hasOtherPaymentMethods = this.hasOtherPaymentMethods(serviceList);
|
|
17244
17464
|
const hasApexxCardInServices = this.hasApexxPaymentMethod(serviceList);
|
|
17245
17465
|
const allServicesAreApexxOnly = this.isOnlyApexxPaymentMethod(serviceList);
|
|
@@ -17257,19 +17477,19 @@ class SearchPaymentValidationService {
|
|
|
17257
17477
|
* Check if any service has non-Apexx payment methods
|
|
17258
17478
|
*/
|
|
17259
17479
|
hasOtherPaymentMethods(serviceList) {
|
|
17260
|
-
return serviceList.some(service => (service.availablePaymentMethods || []).some((method) => method.name !== PaymentMethodType.Apexx));
|
|
17480
|
+
return serviceList.some((service) => (service.availablePaymentMethods || []).some((method) => method.name !== PaymentMethodType.Apexx));
|
|
17261
17481
|
}
|
|
17262
17482
|
/**
|
|
17263
17483
|
* Check if any service has Apexx payment method
|
|
17264
17484
|
*/
|
|
17265
17485
|
hasApexxPaymentMethod(serviceList) {
|
|
17266
|
-
return serviceList.some(service => (service.availablePaymentMethods || []).some((method) => method.name === PaymentMethodType.Apexx));
|
|
17486
|
+
return serviceList.some((service) => (service.availablePaymentMethods || []).some((method) => method.name === PaymentMethodType.Apexx));
|
|
17267
17487
|
}
|
|
17268
17488
|
/**
|
|
17269
17489
|
* Check if all services only have Apexx as payment method
|
|
17270
17490
|
*/
|
|
17271
17491
|
isOnlyApexxPaymentMethod(serviceList) {
|
|
17272
|
-
return serviceList.every(service => {
|
|
17492
|
+
return serviceList.every((service) => {
|
|
17273
17493
|
const methods = service.availablePaymentMethods || [];
|
|
17274
17494
|
if (methods.length === 0)
|
|
17275
17495
|
return false;
|
|
@@ -17285,7 +17505,8 @@ class SearchPaymentValidationService {
|
|
|
17285
17505
|
const isValidList = Array.isArray(serviceList);
|
|
17286
17506
|
const hasOtherPaymentMethods = this.hasOtherPaymentMethods(serviceList);
|
|
17287
17507
|
const hasApexxCardInServices = this.hasApexxPaymentMethod(serviceList);
|
|
17288
|
-
restrictions[key] =
|
|
17508
|
+
restrictions[key] =
|
|
17509
|
+
isValidList && !hasOtherPaymentMethods && hasApexxCardInServices && hasNoStoredCard;
|
|
17289
17510
|
});
|
|
17290
17511
|
return restrictions;
|
|
17291
17512
|
}
|
|
@@ -17343,9 +17564,119 @@ class ResultAwareService {
|
|
|
17343
17564
|
console.log('[ResultAwareService] Captured', outbound.length, 'outbound +', this.railReturnJourneys.length, 'return rail results, sourceId:', this.railSourceId);
|
|
17344
17565
|
this.resultsAvailableSubject.next(this.condensedResults.length > 0);
|
|
17345
17566
|
}
|
|
17567
|
+
captureHotelResults(results) {
|
|
17568
|
+
this.currentServiceType = ServiceType.Hotel;
|
|
17569
|
+
this.rawResults = results;
|
|
17570
|
+
this.condensedResults = this.condenseHotelResults(results);
|
|
17571
|
+
this.resultsAvailableSubject.next(this.condensedResults.length > 0);
|
|
17572
|
+
}
|
|
17573
|
+
isHotelSearch() {
|
|
17574
|
+
return this.currentServiceType === ServiceType.Hotel;
|
|
17575
|
+
}
|
|
17346
17576
|
getCondensedResults() {
|
|
17347
17577
|
return this.condensedResults;
|
|
17348
17578
|
}
|
|
17579
|
+
/**
|
|
17580
|
+
* Returns the cheapest single and return fare prices for a raw outbound rail journey.
|
|
17581
|
+
* Used by the chatbot fare panel to show per-category prices on card expand.
|
|
17582
|
+
*/
|
|
17583
|
+
getRailJourneyFares(journeyIndex) {
|
|
17584
|
+
const journey = this.rawResults[journeyIndex];
|
|
17585
|
+
if (!journey)
|
|
17586
|
+
return {};
|
|
17587
|
+
const singleTickets = this.collectFareTickets(journey, ['singleJourneyFares'], journeyIndex, false);
|
|
17588
|
+
const returnTickets = this.collectFareTickets(journey, ['returnJourneyFares', 'dualSingleJourneyFares', 'openReturnJourneyFares'], journeyIndex, false);
|
|
17589
|
+
return {
|
|
17590
|
+
singlePrice: singleTickets[0]?.price,
|
|
17591
|
+
returnPrice: returnTickets[0]?.price,
|
|
17592
|
+
singleTickets: singleTickets.length ? singleTickets : undefined,
|
|
17593
|
+
returnTickets: returnTickets.length ? returnTickets : undefined
|
|
17594
|
+
};
|
|
17595
|
+
}
|
|
17596
|
+
/**
|
|
17597
|
+
* Returns the cheapest single fare price and full ticket list for a raw inbound/return rail journey.
|
|
17598
|
+
* Falls back to the other return-leg fare sections if no pure singles are available.
|
|
17599
|
+
*/
|
|
17600
|
+
getRailReturnJourneyFares(journeyIndex) {
|
|
17601
|
+
const journey = this.railReturnJourneys[journeyIndex];
|
|
17602
|
+
if (!journey)
|
|
17603
|
+
return {};
|
|
17604
|
+
let singleTickets = this.collectFareTickets(journey, ['singleJourneyFares'], journeyIndex, true);
|
|
17605
|
+
if (!singleTickets.length) {
|
|
17606
|
+
singleTickets = this.collectFareTickets(journey, ['returnJourneyFares', 'dualSingleJourneyFares', 'openReturnJourneyFares'], journeyIndex, true);
|
|
17607
|
+
}
|
|
17608
|
+
return {
|
|
17609
|
+
singlePrice: singleTickets[0]?.price,
|
|
17610
|
+
singleTickets: singleTickets.length ? singleTickets : undefined
|
|
17611
|
+
};
|
|
17612
|
+
}
|
|
17613
|
+
/**
|
|
17614
|
+
* Open the existing OBT rail "View Conditions" dialog for a fare displayed in the chat.
|
|
17615
|
+
*/
|
|
17616
|
+
async openRailFareDetails(ticket) {
|
|
17617
|
+
if (!ticket?.fareHash || ticket.section == null || ticket.journeyIndex == null)
|
|
17618
|
+
return;
|
|
17619
|
+
const journey = ticket.isReturnLeg
|
|
17620
|
+
? this.railReturnJourneys[ticket.journeyIndex]
|
|
17621
|
+
: this.rawResults[ticket.journeyIndex];
|
|
17622
|
+
if (!journey)
|
|
17623
|
+
return;
|
|
17624
|
+
const fareGroup = journey[ticket.section];
|
|
17625
|
+
if (!fareGroup)
|
|
17626
|
+
return;
|
|
17627
|
+
let foundFare = null;
|
|
17628
|
+
for (const key of Object.keys(fareGroup)) {
|
|
17629
|
+
const fares = fareGroup[key];
|
|
17630
|
+
if (!Array.isArray(fares))
|
|
17631
|
+
continue;
|
|
17632
|
+
foundFare = fares.find((f) => f?.fareHash === ticket.fareHash);
|
|
17633
|
+
if (foundFare)
|
|
17634
|
+
break;
|
|
17635
|
+
}
|
|
17636
|
+
if (!foundFare)
|
|
17637
|
+
return;
|
|
17638
|
+
const searchParams = this.searchService.searches[ServiceType.Rail];
|
|
17639
|
+
if (typeof searchParams?.openRailTicketDetailsDialog !== 'function')
|
|
17640
|
+
return;
|
|
17641
|
+
await searchParams.openRailTicketDetailsDialog(journey, foundFare, foundFare.terms, foundFare.identifiers, this.railSourceId || '');
|
|
17642
|
+
}
|
|
17643
|
+
collectFareTickets(journey, sections, journeyIndex, isReturnLeg) {
|
|
17644
|
+
const tickets = [];
|
|
17645
|
+
for (const section of sections) {
|
|
17646
|
+
const fareGroup = journey[section];
|
|
17647
|
+
if (!fareGroup || typeof fareGroup !== 'object')
|
|
17648
|
+
continue;
|
|
17649
|
+
for (const fareCode of Object.keys(fareGroup)) {
|
|
17650
|
+
const fares = fareGroup[fareCode];
|
|
17651
|
+
if (!Array.isArray(fares))
|
|
17652
|
+
continue;
|
|
17653
|
+
for (const fare of fares) {
|
|
17654
|
+
if (!fare || fare.unavailable || !fare.selectable || !(fare.price > 0))
|
|
17655
|
+
continue;
|
|
17656
|
+
tickets.push({
|
|
17657
|
+
type: fare.type || fare.fareName || fareCode || 'Ticket',
|
|
17658
|
+
classOfService: fare.class,
|
|
17659
|
+
price: fare.price,
|
|
17660
|
+
currency: fare.currency || 'GBP',
|
|
17661
|
+
fareHash: fare.fareHash,
|
|
17662
|
+
section,
|
|
17663
|
+
isReturnLeg,
|
|
17664
|
+
journeyIndex
|
|
17665
|
+
});
|
|
17666
|
+
}
|
|
17667
|
+
}
|
|
17668
|
+
}
|
|
17669
|
+
// Sort cheapest first, then dedupe identical type+class+price entries
|
|
17670
|
+
tickets.sort((a, b) => a.price - b.price);
|
|
17671
|
+
const seen = new Set();
|
|
17672
|
+
return tickets.filter((t) => {
|
|
17673
|
+
const key = `${t.type}|${t.classOfService}|${t.price}`;
|
|
17674
|
+
if (seen.has(key))
|
|
17675
|
+
return false;
|
|
17676
|
+
seen.add(key);
|
|
17677
|
+
return true;
|
|
17678
|
+
});
|
|
17679
|
+
}
|
|
17349
17680
|
hasResults() {
|
|
17350
17681
|
return this.condensedResults.length > 0;
|
|
17351
17682
|
}
|
|
@@ -17356,6 +17687,15 @@ class ResultAwareService {
|
|
|
17356
17687
|
this.resultsAvailableSubject.next(false);
|
|
17357
17688
|
}
|
|
17358
17689
|
async executeSelection(selectedIndex, returnIndex) {
|
|
17690
|
+
if (this.currentServiceType === ServiceType.Hotel) {
|
|
17691
|
+
// Hotel indices are 1-based (matching natural user language: "option 1, 2, 3")
|
|
17692
|
+
const hotelArrayIndex = selectedIndex - 1;
|
|
17693
|
+
if (hotelArrayIndex < 0 || hotelArrayIndex >= this.rawResults.length) {
|
|
17694
|
+
throw new Error(`Invalid selection index: ${selectedIndex}`);
|
|
17695
|
+
}
|
|
17696
|
+
await this.executeHotelSelection(this.rawResults[hotelArrayIndex]);
|
|
17697
|
+
return;
|
|
17698
|
+
}
|
|
17359
17699
|
if (selectedIndex < 0 || selectedIndex >= this.rawResults.length) {
|
|
17360
17700
|
throw new Error(`Invalid selection index: ${selectedIndex}`);
|
|
17361
17701
|
}
|
|
@@ -17436,7 +17776,7 @@ class ResultAwareService {
|
|
|
17436
17776
|
flightNumbers: inLegs
|
|
17437
17777
|
.map((leg) => `${leg.marketingCarrier || ''}${leg.marketingFlightNumber || ''}`.trim())
|
|
17438
17778
|
.filter(Boolean)
|
|
17439
|
-
.join('/')
|
|
17779
|
+
.join('/')
|
|
17440
17780
|
};
|
|
17441
17781
|
}
|
|
17442
17782
|
return {
|
|
@@ -17455,7 +17795,7 @@ class ResultAwareService {
|
|
|
17455
17795
|
to: lastLeg?.destinationAirport || '',
|
|
17456
17796
|
flightNumbers,
|
|
17457
17797
|
fares: [{ cabin, price, bags }],
|
|
17458
|
-
inbound
|
|
17798
|
+
inbound
|
|
17459
17799
|
};
|
|
17460
17800
|
});
|
|
17461
17801
|
}
|
|
@@ -17465,7 +17805,11 @@ class ResultAwareService {
|
|
|
17465
17805
|
}
|
|
17466
17806
|
try {
|
|
17467
17807
|
const date = new Date(isoString);
|
|
17468
|
-
return date.toLocaleTimeString('en-GB', {
|
|
17808
|
+
return date.toLocaleTimeString('en-GB', {
|
|
17809
|
+
hour: '2-digit',
|
|
17810
|
+
minute: '2-digit',
|
|
17811
|
+
hour12: false
|
|
17812
|
+
});
|
|
17469
17813
|
}
|
|
17470
17814
|
catch {
|
|
17471
17815
|
return isoString;
|
|
@@ -17479,6 +17823,19 @@ class ResultAwareService {
|
|
|
17479
17823
|
const m = minutes % 60;
|
|
17480
17824
|
return `${h}h${m > 0 ? ` ${m}m` : ''}`;
|
|
17481
17825
|
}
|
|
17826
|
+
computeRailDuration(rj) {
|
|
17827
|
+
if (rj?.journeyDuration) {
|
|
17828
|
+
return this.formatDuration(rj.journeyDuration);
|
|
17829
|
+
}
|
|
17830
|
+
if (rj?.departDateTime && rj?.arriveDateTime) {
|
|
17831
|
+
const dep = new Date(rj.departDateTime).getTime();
|
|
17832
|
+
const arr = new Date(rj.arriveDateTime).getTime();
|
|
17833
|
+
if (!isNaN(dep) && !isNaN(arr) && arr > dep) {
|
|
17834
|
+
return this.formatDuration(Math.round((arr - dep) / 60000));
|
|
17835
|
+
}
|
|
17836
|
+
}
|
|
17837
|
+
return '';
|
|
17838
|
+
}
|
|
17482
17839
|
async executeRailSelection(journey, returnIndex) {
|
|
17483
17840
|
const searchParams = this.searchService.searches[ServiceType.Rail];
|
|
17484
17841
|
// Find the cheapest available fare from the journey
|
|
@@ -17492,7 +17849,9 @@ class ResultAwareService {
|
|
|
17492
17849
|
let inboundJourney = null;
|
|
17493
17850
|
let inboundFare = null;
|
|
17494
17851
|
if (this.railReturnJourneys.length > 0) {
|
|
17495
|
-
if (returnIndex !== undefined &&
|
|
17852
|
+
if (returnIndex !== undefined &&
|
|
17853
|
+
returnIndex >= 0 &&
|
|
17854
|
+
returnIndex < this.railReturnJourneys.length) {
|
|
17496
17855
|
inboundJourney = this.railReturnJourneys[returnIndex];
|
|
17497
17856
|
}
|
|
17498
17857
|
else {
|
|
@@ -17505,7 +17864,8 @@ class ResultAwareService {
|
|
|
17505
17864
|
// Use addToBasket with ignoreTicketExtras=true to skip the travelcard add-on dialog
|
|
17506
17865
|
// and actually add the item to the basket (selectTicketNoAdd only stores selection without basketing)
|
|
17507
17866
|
await searchParams.addToBasket(sourceId, searchParams.originalUserSearch, journey, fare, inboundFare || undefined, inboundJourney || undefined, false, // ingoreInvoked
|
|
17508
|
-
true
|
|
17867
|
+
true // ignoreTicketExtras — skip travelcard dialog
|
|
17868
|
+
);
|
|
17509
17869
|
// Navigate to confirmation
|
|
17510
17870
|
const basket = (await this.basketService.getDefaultBasket())?.subject.value;
|
|
17511
17871
|
if (basket?.id) {
|
|
@@ -17529,7 +17889,7 @@ class ResultAwareService {
|
|
|
17529
17889
|
'returnJourneyFares',
|
|
17530
17890
|
'singleJourneyFares',
|
|
17531
17891
|
'dualSingleJourneyFares',
|
|
17532
|
-
'openReturnJourneyFares'
|
|
17892
|
+
'openReturnJourneyFares'
|
|
17533
17893
|
];
|
|
17534
17894
|
let cheapest = null;
|
|
17535
17895
|
let cheapestSection = '';
|
|
@@ -17564,7 +17924,7 @@ class ResultAwareService {
|
|
|
17564
17924
|
'returnJourneyFares',
|
|
17565
17925
|
'singleJourneyFares',
|
|
17566
17926
|
'dualSingleJourneyFares',
|
|
17567
|
-
'openReturnJourneyFares'
|
|
17927
|
+
'openReturnJourneyFares'
|
|
17568
17928
|
];
|
|
17569
17929
|
let expensive = null;
|
|
17570
17930
|
let expensiveSection = '';
|
|
@@ -17601,15 +17961,17 @@ class ResultAwareService {
|
|
|
17601
17961
|
const rj = returnJourneys[i];
|
|
17602
17962
|
const cheapestFare = this.getCheapestRailFare(rj);
|
|
17603
17963
|
const expensiveFare = this.getMostExpensiveRailFare(rj);
|
|
17604
|
-
const cheapestValid = cheapestFare &&
|
|
17605
|
-
|
|
17964
|
+
const cheapestValid = cheapestFare &&
|
|
17965
|
+
(cheapestFare.price > 0 || cheapestFare.type.toLowerCase().includes('return'));
|
|
17966
|
+
const expensiveValid = expensiveFare &&
|
|
17967
|
+
(expensiveFare.price > 0 || expensiveFare.type.toLowerCase().includes('return'));
|
|
17606
17968
|
if (cheapestFare) {
|
|
17607
17969
|
condensedReturns.push({
|
|
17608
17970
|
index: i,
|
|
17609
17971
|
depart: this.formatTime(rj.departDateTime),
|
|
17610
17972
|
arrive: this.formatTime(rj.arriveDateTime),
|
|
17611
17973
|
stops: parseInt(rj.changes || '0', 10),
|
|
17612
|
-
duration: this.
|
|
17974
|
+
duration: this.computeRailDuration(rj),
|
|
17613
17975
|
price: cheapestValid ? cheapestFare.price : 0,
|
|
17614
17976
|
operator: rj.journeyLegs?.[0]?.operatorName || '',
|
|
17615
17977
|
policyStatus: cheapestValid
|
|
@@ -17625,7 +17987,7 @@ class ResultAwareService {
|
|
|
17625
17987
|
depart: this.formatTime(rj.departDateTime),
|
|
17626
17988
|
arrive: this.formatTime(rj.arriveDateTime),
|
|
17627
17989
|
stops: parseInt(rj.changes || '0', 10),
|
|
17628
|
-
duration: this.
|
|
17990
|
+
duration: this.computeRailDuration(rj),
|
|
17629
17991
|
price: expensiveValid ? expensiveFare.price : 0,
|
|
17630
17992
|
operator: rj.journeyLegs?.[0]?.operatorName || '',
|
|
17631
17993
|
policyStatus: expensiveValid
|
|
@@ -17647,7 +18009,7 @@ class ResultAwareService {
|
|
|
17647
18009
|
depart: this.formatTime(journey.departDateTime),
|
|
17648
18010
|
arrive: this.formatTime(journey.arriveDateTime),
|
|
17649
18011
|
stops: parseInt(journey.changes || '0', 10),
|
|
17650
|
-
duration: this.
|
|
18012
|
+
duration: this.computeRailDuration(journey),
|
|
17651
18013
|
price: hasValidFare ? cheapestFare.price : 0,
|
|
17652
18014
|
cabin: cheapestFare?.class || 'Standard',
|
|
17653
18015
|
co2: journey.co2PerPassenger || 0,
|
|
@@ -17661,14 +18023,15 @@ class ResultAwareService {
|
|
|
17661
18023
|
}
|
|
17662
18024
|
parsdJourneys.push(result);
|
|
17663
18025
|
const expensiveFare = this.getMostExpensiveRailFare(journey);
|
|
17664
|
-
const expensiveValid = expensiveFare &&
|
|
18026
|
+
const expensiveValid = expensiveFare &&
|
|
18027
|
+
(expensiveFare.price > 0 || expensiveFare.type.toLowerCase().includes('return'));
|
|
17665
18028
|
const exResult = {
|
|
17666
18029
|
index: i,
|
|
17667
18030
|
operator: journey.journeyLegs?.[0]?.operatorName || '',
|
|
17668
18031
|
depart: this.formatTime(journey.departDateTime),
|
|
17669
18032
|
arrive: this.formatTime(journey.arriveDateTime),
|
|
17670
18033
|
stops: parseInt(journey.changes || '0', 10),
|
|
17671
|
-
duration: this.
|
|
18034
|
+
duration: this.computeRailDuration(journey),
|
|
17672
18035
|
price: expensiveValid ? expensiveFare.price : 0,
|
|
17673
18036
|
cabin: expensiveFare?.class || 'Standard',
|
|
17674
18037
|
co2: journey.co2PerPassenger || 0,
|
|
@@ -17716,9 +18079,9 @@ class ResultAwareService {
|
|
|
17716
18079
|
return '';
|
|
17717
18080
|
}
|
|
17718
18081
|
filterDownCompressedReturnRailOptions(result, condensedReturns = []) {
|
|
17719
|
-
condensedReturns = condensedReturns.filter(ret => ret.singleOrReturn === result.singleOrReturn);
|
|
18082
|
+
condensedReturns = condensedReturns.filter((ret) => ret.singleOrReturn === result.singleOrReturn);
|
|
17720
18083
|
if (result.singleOrReturn === 'Return') {
|
|
17721
|
-
condensedReturns.forEach(ret => {
|
|
18084
|
+
condensedReturns.forEach((ret) => {
|
|
17722
18085
|
ret.price = 0;
|
|
17723
18086
|
});
|
|
17724
18087
|
}
|
|
@@ -17727,7 +18090,7 @@ class ResultAwareService {
|
|
|
17727
18090
|
const arrivalDate = moment$1();
|
|
17728
18091
|
arrivalDate.hour(arrivalTimeSplit[0]);
|
|
17729
18092
|
arrivalDate.minutes(arrivalTimeSplit[1]);
|
|
17730
|
-
condensedReturns = condensedReturns.filter(ret => {
|
|
18093
|
+
condensedReturns = condensedReturns.filter((ret) => {
|
|
17731
18094
|
try {
|
|
17732
18095
|
const departTimeSplit = ret.depart.split(':');
|
|
17733
18096
|
const retDepartTime = moment$1();
|
|
@@ -17741,6 +18104,81 @@ class ResultAwareService {
|
|
|
17741
18104
|
});
|
|
17742
18105
|
return condensedReturns;
|
|
17743
18106
|
}
|
|
18107
|
+
async selectHotelRoom(hotelIndex, roomId) {
|
|
18108
|
+
const hotel = this.rawResults[hotelIndex];
|
|
18109
|
+
if (!hotel) {
|
|
18110
|
+
return;
|
|
18111
|
+
}
|
|
18112
|
+
const searchParams = this.searchService.searches[ServiceType.Hotel];
|
|
18113
|
+
const room = (hotel.availableRates?.rooms ?? []).find((r) => r?.roomId === roomId);
|
|
18114
|
+
if (!room) {
|
|
18115
|
+
return;
|
|
18116
|
+
}
|
|
18117
|
+
// Rooms in rawResults may lack policyToken if JIT rules completed after the snapshot.
|
|
18118
|
+
// Look up the matching room from fullResults (JIT-processed) and carry policyToken across.
|
|
18119
|
+
if (!room.policyToken) {
|
|
18120
|
+
const fullHotel = (searchParams.fullResults ?? []).find((h) => h.id === hotel.id);
|
|
18121
|
+
const fullRoom = (fullHotel?.availableRates?.rooms ?? []).find((r) => r?.roomId === roomId);
|
|
18122
|
+
if (fullRoom?.policyToken) {
|
|
18123
|
+
room.policyToken = fullRoom.policyToken;
|
|
18124
|
+
}
|
|
18125
|
+
}
|
|
18126
|
+
await searchParams.addHotelRoomToBasket(hotel, room, searchParams.getSearchQuery(true));
|
|
18127
|
+
this.basketService.toggleMenu();
|
|
18128
|
+
const basket = (await this.basketService.getDefaultBasket())?.subject.value;
|
|
18129
|
+
if (basket?.id) {
|
|
18130
|
+
this.router.navigate(['/itinerary', basket.id]);
|
|
18131
|
+
}
|
|
18132
|
+
}
|
|
18133
|
+
async executeHotelSelection(hotel) {
|
|
18134
|
+
const searchParams = this.searchService.searches[ServiceType.Hotel];
|
|
18135
|
+
const rooms = hotel.availableRates?.rooms ?? [];
|
|
18136
|
+
const cheapestRoom = rooms
|
|
18137
|
+
.filter((r) => r && !r.unavailable)
|
|
18138
|
+
.sort((a, b) => a.total - b.total)[0];
|
|
18139
|
+
if (!cheapestRoom) {
|
|
18140
|
+
return;
|
|
18141
|
+
}
|
|
18142
|
+
await searchParams.addHotelRoomToBasket(hotel, cheapestRoom, searchParams.getSearchQuery(true));
|
|
18143
|
+
this.basketService.toggleMenu();
|
|
18144
|
+
const basket = (await this.basketService.getDefaultBasket())?.subject.value;
|
|
18145
|
+
if (basket?.id) {
|
|
18146
|
+
this.router.navigate(['/itinerary', basket.id]);
|
|
18147
|
+
}
|
|
18148
|
+
}
|
|
18149
|
+
condenseHotelResults(results) {
|
|
18150
|
+
const hotelSearch = this.searchService.searches[ServiceType.Hotel];
|
|
18151
|
+
const checkin = hotelSearch?.checkin_date
|
|
18152
|
+
? moment$1(hotelSearch.checkin_date).format('D MMM')
|
|
18153
|
+
: '';
|
|
18154
|
+
const checkout = hotelSearch?.checkout_date
|
|
18155
|
+
? moment$1(hotelSearch.checkout_date).format('D MMM')
|
|
18156
|
+
: '';
|
|
18157
|
+
const dateRange = checkin && checkout ? `${checkin} - ${checkout}` : '';
|
|
18158
|
+
return results.map((hotel, index) => {
|
|
18159
|
+
const rooms = hotel.availableRates?.rooms ?? [];
|
|
18160
|
+
const cheapestRoom = rooms
|
|
18161
|
+
.filter((r) => r && !r.unavailable)
|
|
18162
|
+
.sort((a, b) => a.total - b.total)[0];
|
|
18163
|
+
return {
|
|
18164
|
+
index: index + 1, // 1-based so user "option 2" maps directly to selectedIndex: 2
|
|
18165
|
+
name: hotel.name || '',
|
|
18166
|
+
address: [hotel.address?.addressLine1, hotel.address?.city].filter(Boolean).join(', '),
|
|
18167
|
+
price: cheapestRoom?.total ?? 0,
|
|
18168
|
+
pricePerNight: cheapestRoom?.prpn ?? 0,
|
|
18169
|
+
rating: hotel.starRating ?? hotel.rating ?? 0,
|
|
18170
|
+
distance: hotel.location?.distance ?? 0,
|
|
18171
|
+
distanceUnit: hotel.location?.distanceType ?? 'M',
|
|
18172
|
+
policyStatus: hotel.policyStatus,
|
|
18173
|
+
policyMessages: hotel.policyMessages,
|
|
18174
|
+
co2: hotel.co2PerItem,
|
|
18175
|
+
depart: checkin,
|
|
18176
|
+
arrive: checkout,
|
|
18177
|
+
stops: 0,
|
|
18178
|
+
duration: dateRange
|
|
18179
|
+
};
|
|
18180
|
+
});
|
|
18181
|
+
}
|
|
17744
18182
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ResultAwareService, deps: [{ token: EnterpriseSearchService }, { token: EnterpriseBasketService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
17745
18183
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ResultAwareService, providedIn: 'root' });
|
|
17746
18184
|
}
|
|
@@ -17758,17 +18196,57 @@ class ChatbotService extends BaseChatbotService {
|
|
|
17758
18196
|
messages$ = this.messagesSubject.asObservable();
|
|
17759
18197
|
loadingSubject = new BehaviorSubject(false);
|
|
17760
18198
|
loading$ = this.loadingSubject.asObservable();
|
|
18199
|
+
searchingSubject = new BehaviorSubject({
|
|
18200
|
+
searching: false
|
|
18201
|
+
});
|
|
18202
|
+
searchingState$ = this.searchingSubject.asObservable();
|
|
18203
|
+
/** Back-compat boolean stream for callers that only care whether a search is in flight. */
|
|
18204
|
+
searching$ = this.searchingSubject.pipe(map((s) => s.searching));
|
|
17761
18205
|
fillSearchForm$ = new Subject();
|
|
17762
18206
|
riskFlagSubject = new BehaviorSubject(null);
|
|
17763
18207
|
riskFlag$ = this.riskFlagSubject.asObservable();
|
|
18208
|
+
faqsSubject = new BehaviorSubject([]);
|
|
18209
|
+
faqs$ = this.faqsSubject.asObservable();
|
|
18210
|
+
// ENT-16247 — streaming + feedback subjects. Use Subject (not Behaviour)
|
|
18211
|
+
// so late subscribers don't accidentally replay the last token chunk
|
|
18212
|
+
// from a previous turn.
|
|
18213
|
+
tokenEventsSubject = new Subject();
|
|
18214
|
+
tokenEvents$ = this.tokenEventsSubject.asObservable();
|
|
18215
|
+
toolEventsSubject = new Subject();
|
|
18216
|
+
toolEvents$ = this.toolEventsSubject.asObservable();
|
|
18217
|
+
feedbackAckSubject = new Subject();
|
|
18218
|
+
feedbackAck$ = this.feedbackAckSubject.asObservable();
|
|
18219
|
+
/** Tracks the user ID that owns the current chat session, to detect user switches. */
|
|
18220
|
+
sessionOwnerId = null;
|
|
18221
|
+
userChangeSubscription;
|
|
17764
18222
|
constructor(webTokenService, resultAwareService, environment, userService) {
|
|
17765
18223
|
super();
|
|
17766
18224
|
this.webTokenService = webTokenService;
|
|
17767
18225
|
this.resultAwareService = resultAwareService;
|
|
17768
18226
|
this.environment = environment;
|
|
17769
18227
|
this.userService = userService;
|
|
18228
|
+
// Tear down the socket on logout (null user) so the next login reconnects cleanly.
|
|
18229
|
+
// Clear the session only when a different user logs in, so the same user's
|
|
18230
|
+
// conversation persists across logout/login cycles.
|
|
18231
|
+
this.userChangeSubscription = this.userService.fullUserDetails.allDetails
|
|
18232
|
+
.pipe(map((user) => user?.id ?? null), distinctUntilChanged())
|
|
18233
|
+
.subscribe((userId) => {
|
|
18234
|
+
if (userId === null) {
|
|
18235
|
+
// User logged out — disconnect socket but keep session data
|
|
18236
|
+
this.tearDownSocket();
|
|
18237
|
+
}
|
|
18238
|
+
else if (this.sessionOwnerId !== null && this.sessionOwnerId !== userId) {
|
|
18239
|
+
// Different user logged in — start fresh
|
|
18240
|
+
this.clearSession();
|
|
18241
|
+
this.tearDownSocket();
|
|
18242
|
+
}
|
|
18243
|
+
if (userId !== null) {
|
|
18244
|
+
this.sessionOwnerId = userId;
|
|
18245
|
+
}
|
|
18246
|
+
});
|
|
17770
18247
|
}
|
|
17771
18248
|
ngOnDestroy() {
|
|
18249
|
+
this.userChangeSubscription.unsubscribe();
|
|
17772
18250
|
this.tearDownSocket();
|
|
17773
18251
|
this.fillSearchForm$.complete();
|
|
17774
18252
|
}
|
|
@@ -17791,6 +18269,18 @@ class ChatbotService extends BaseChatbotService {
|
|
|
17791
18269
|
notifyRiskFlag(flag) {
|
|
17792
18270
|
this.riskFlagSubject.next(flag);
|
|
17793
18271
|
}
|
|
18272
|
+
notifyFaqs(faqs) {
|
|
18273
|
+
this.faqsSubject.next(faqs);
|
|
18274
|
+
}
|
|
18275
|
+
notifyToken(event) {
|
|
18276
|
+
this.tokenEventsSubject.next(event);
|
|
18277
|
+
}
|
|
18278
|
+
notifyTool(event) {
|
|
18279
|
+
this.toolEventsSubject.next(event);
|
|
18280
|
+
}
|
|
18281
|
+
notifyFeedbackAck(ack) {
|
|
18282
|
+
this.feedbackAckSubject.next(ack);
|
|
18283
|
+
}
|
|
17794
18284
|
hasResults() {
|
|
17795
18285
|
return this.resultAwareService.hasResults();
|
|
17796
18286
|
}
|
|
@@ -17803,20 +18293,53 @@ class ChatbotService extends BaseChatbotService {
|
|
|
17803
18293
|
getSource() {
|
|
17804
18294
|
return 'web';
|
|
17805
18295
|
}
|
|
17806
|
-
// ── Public override ──────────────────────────────────────────────────────
|
|
17807
|
-
clearSession() {
|
|
17808
|
-
super.clearSession();
|
|
17809
|
-
this.resultAwareService.clearResults();
|
|
17810
|
-
}
|
|
17811
18296
|
getUserCapabilities() {
|
|
17812
18297
|
return {
|
|
17813
18298
|
isCrown: this.userService.crownIsSet(),
|
|
17814
18299
|
isAgent: this.userService.userIsAgent(),
|
|
17815
18300
|
uiConfigs: {
|
|
17816
|
-
hideHotelSearchOptions: this.userService.isUserFavoriteSet(new UserFavorurite('hideHotelSearchOptions'))
|
|
17817
|
-
}
|
|
18301
|
+
hideHotelSearchOptions: this.userService.isUserFavoriteSet(new UserFavorurite('hideHotelSearchOptions'))
|
|
18302
|
+
}
|
|
17818
18303
|
};
|
|
17819
18304
|
}
|
|
18305
|
+
// ── Public override ──────────────────────────────────────────────────────
|
|
18306
|
+
connect() {
|
|
18307
|
+
const user = this.userService.fullUserDetails.allDetails.value;
|
|
18308
|
+
if (!user?.aiTravelAssistantEnabled) {
|
|
18309
|
+
return;
|
|
18310
|
+
}
|
|
18311
|
+
super.connect();
|
|
18312
|
+
}
|
|
18313
|
+
clearSession() {
|
|
18314
|
+
super.clearSession();
|
|
18315
|
+
this.resultAwareService.clearResults();
|
|
18316
|
+
}
|
|
18317
|
+
injectMessage(message) {
|
|
18318
|
+
this.addMessage(message);
|
|
18319
|
+
}
|
|
18320
|
+
handleResponse(env) {
|
|
18321
|
+
super.handleResponse(env);
|
|
18322
|
+
// Auto-trigger a search as soon as the bot signals parameters are complete.
|
|
18323
|
+
// The dispatcher routes to the travel-type handler, which owns the search +
|
|
18324
|
+
// result-capture flow.
|
|
18325
|
+
const tt = env.parameters?.travelType;
|
|
18326
|
+
if (env.state === 'PARAMS_COMPLETE' &&
|
|
18327
|
+
(tt === 'rail' || tt === 'flight' || tt === 'hotel' || tt === 'eurostar')) {
|
|
18328
|
+
this.notifyFillForm(env.parameters, true);
|
|
18329
|
+
}
|
|
18330
|
+
}
|
|
18331
|
+
// ── Public helpers for travel-type handlers ──────────────────────────────
|
|
18332
|
+
/**
|
|
18333
|
+
* Toggle the chatbot's "searching" indicator from a travel-type handler.
|
|
18334
|
+
* @param travelType optional - lets the UI render a travel-aware label
|
|
18335
|
+
* (e.g. "Searching for flights…" vs "Searching for trains…").
|
|
18336
|
+
*/
|
|
18337
|
+
setSearching(searching, travelType) {
|
|
18338
|
+
this.searchingSubject.next({ searching, travelType });
|
|
18339
|
+
}
|
|
18340
|
+
patchAssistantMessage(patch) {
|
|
18341
|
+
this.patchLastAssistantMessage(patch);
|
|
18342
|
+
}
|
|
17820
18343
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotService, deps: [{ token: WebTokenService }, { token: ResultAwareService }, { token: Environment }, { token: UserService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
17821
18344
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotService, providedIn: 'root' });
|
|
17822
18345
|
}
|
|
@@ -17825,6 +18348,2070 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
17825
18348
|
args: [{ providedIn: 'root' }]
|
|
17826
18349
|
}], ctorParameters: () => [{ type: WebTokenService }, { type: ResultAwareService }, { type: Environment }, { type: UserService }] });
|
|
17827
18350
|
|
|
18351
|
+
/** How long (ms) to wait for rail results before giving up on patching the message. */
|
|
18352
|
+
const RAIL_RESULTS_WAIT_MS = 90_000;
|
|
18353
|
+
/** Maximum number of journey cards to render per leg. */
|
|
18354
|
+
const MAX_RAIL_CARDS_PER_LEG = 3;
|
|
18355
|
+
/**
|
|
18356
|
+
* Handles chatbot-driven rail searches. Registered with
|
|
18357
|
+
* `ChatbotSearchDispatcherService`; not used directly.
|
|
18358
|
+
*
|
|
18359
|
+
* Populates the rail search state from the chatbot's parameters (same state
|
|
18360
|
+
* the search form binds to via ngModel) and runs `startSearch()` headlessly
|
|
18361
|
+
* so chatbot rail searches work from any page. Once results arrive, builds
|
|
18362
|
+
* `ChatRailResultCard`s and patches them onto the last assistant message
|
|
18363
|
+
* via {@link ChatbotService.patchAssistantMessage}.
|
|
18364
|
+
*/
|
|
18365
|
+
class ChatbotRailSearchService {
|
|
18366
|
+
chatbotService;
|
|
18367
|
+
searchService;
|
|
18368
|
+
resultAwareService;
|
|
18369
|
+
userService;
|
|
18370
|
+
travelType = 'rail';
|
|
18371
|
+
resultsSubscription = null;
|
|
18372
|
+
constructor(chatbotService, searchService, resultAwareService, userService) {
|
|
18373
|
+
this.chatbotService = chatbotService;
|
|
18374
|
+
this.searchService = searchService;
|
|
18375
|
+
this.resultAwareService = resultAwareService;
|
|
18376
|
+
this.userService = userService;
|
|
18377
|
+
}
|
|
18378
|
+
async handle(parameters, autoSearch) {
|
|
18379
|
+
this.searchService.resetSearchChosenObjects();
|
|
18380
|
+
this.searchService.resetSearchPriorities();
|
|
18381
|
+
this.searchService.search_objects[ServiceType.Rail].chosen = true;
|
|
18382
|
+
this.searchService.search_objects[ServiceType.Rail].originalChosen = true;
|
|
18383
|
+
this.searchService.determineHighestSearchPriority();
|
|
18384
|
+
if (this.searchService.travellerInformation.value.length === 0) {
|
|
18385
|
+
const selfTraveller = this.userService.createSelfTraveller();
|
|
18386
|
+
if (selfTraveller) {
|
|
18387
|
+
this.searchService.addTraveller(selfTraveller);
|
|
18388
|
+
}
|
|
18389
|
+
}
|
|
18390
|
+
const departDate = moment$1(parameters.departureDate);
|
|
18391
|
+
const departNgbDate = {
|
|
18392
|
+
year: departDate.year(),
|
|
18393
|
+
month: departDate.month() + 1,
|
|
18394
|
+
day: departDate.date()
|
|
18395
|
+
};
|
|
18396
|
+
if (autoSearch) {
|
|
18397
|
+
// Attach the results subscription before kicking off the search so we
|
|
18398
|
+
// can't miss the resultsAvailable$ emission. Synchronous — safe.
|
|
18399
|
+
this.subscribeToRailResults(parameters);
|
|
18400
|
+
}
|
|
18401
|
+
await this.populateRailParams(parameters, departNgbDate);
|
|
18402
|
+
if (!autoSearch) {
|
|
18403
|
+
return;
|
|
18404
|
+
}
|
|
18405
|
+
const railSearch = this.searchService.searches[ServiceType.Rail];
|
|
18406
|
+
// Surface OBT's existing validation (past dates, missing stations,
|
|
18407
|
+
// same origin/destination, return-before-depart, etc.) instead of letting
|
|
18408
|
+
// the user wait for a search that will silently no-op. Every setter on
|
|
18409
|
+
// RailEnterpriseSearch runs `_isValid()` so the array is current by the
|
|
18410
|
+
// time `populateRailParams` returns.
|
|
18411
|
+
const validationMessages = Array.isArray(railSearch.validationMessages)
|
|
18412
|
+
? railSearch.validationMessages.filter(Boolean)
|
|
18413
|
+
: [];
|
|
18414
|
+
if (validationMessages.length) {
|
|
18415
|
+
this.resultsSubscription?.unsubscribe();
|
|
18416
|
+
this.resultsSubscription = null;
|
|
18417
|
+
this.chatbotService.patchAssistantMessage({
|
|
18418
|
+
text: this.formatValidationMessages(validationMessages),
|
|
18419
|
+
state: 'RESULTS_READY'
|
|
18420
|
+
});
|
|
18421
|
+
this.chatbotService.setSearching(false);
|
|
18422
|
+
return;
|
|
18423
|
+
}
|
|
18424
|
+
const ok = await railSearch.startSearch();
|
|
18425
|
+
if (!ok) {
|
|
18426
|
+
// Safety net — should rarely fire now that we read validationMessages
|
|
18427
|
+
// up-front, but covers any later-rejecting validation path.
|
|
18428
|
+
this.resultsSubscription?.unsubscribe();
|
|
18429
|
+
this.resultsSubscription = null;
|
|
18430
|
+
const messages = Array.isArray(railSearch.validationMessages)
|
|
18431
|
+
? railSearch.validationMessages.filter(Boolean)
|
|
18432
|
+
: [];
|
|
18433
|
+
this.chatbotService.patchAssistantMessage({
|
|
18434
|
+
text: messages.length
|
|
18435
|
+
? this.formatValidationMessages(messages)
|
|
18436
|
+
: `I couldn't run that search — please check the dates and stations and try again.`,
|
|
18437
|
+
state: 'RESULTS_READY'
|
|
18438
|
+
});
|
|
18439
|
+
this.chatbotService.setSearching(false);
|
|
18440
|
+
return;
|
|
18441
|
+
}
|
|
18442
|
+
const railResults = railSearch.results.value;
|
|
18443
|
+
if (railResults?.[0]) {
|
|
18444
|
+
this.resultAwareService.captureRailResults(railResults[0]);
|
|
18445
|
+
}
|
|
18446
|
+
else {
|
|
18447
|
+
console.warn('[ChatbotRailSearchService] no rail results to capture (startSearch returned', ok, ')');
|
|
18448
|
+
this.chatbotService.setSearching(false);
|
|
18449
|
+
}
|
|
18450
|
+
}
|
|
18451
|
+
/**
|
|
18452
|
+
* Render OBT's validationMessages array as a chatbot reply. Single message →
|
|
18453
|
+
* inline sentence; multiple → bullet list so each issue reads clearly.
|
|
18454
|
+
*/
|
|
18455
|
+
formatValidationMessages(messages) {
|
|
18456
|
+
if (messages.length === 1) {
|
|
18457
|
+
return `I couldn't run that search — ${messages[0]}. Please adjust and try again.`;
|
|
18458
|
+
}
|
|
18459
|
+
const bullets = messages.map((m) => `• ${m}`).join('\n');
|
|
18460
|
+
return `I couldn't run that search — please fix the following and try again:\n${bullets}`;
|
|
18461
|
+
}
|
|
18462
|
+
// ── Result capture + card building ───────────────────────────────────────
|
|
18463
|
+
/**
|
|
18464
|
+
* Subscribe to ResultAwareService for the next rail result emission, build
|
|
18465
|
+
* the top-N cards per leg, then patch the last assistant message so the
|
|
18466
|
+
* chat widget can render them.
|
|
18467
|
+
*/
|
|
18468
|
+
subscribeToRailResults(parameters) {
|
|
18469
|
+
this.resultsSubscription?.unsubscribe();
|
|
18470
|
+
// Reset the BehaviorSubject so a second search doesn't fire the take(1)
|
|
18471
|
+
// subscriber immediately with stale data and then never update.
|
|
18472
|
+
this.resultAwareService.clearResults();
|
|
18473
|
+
this.chatbotService.setSearching(true, 'rail');
|
|
18474
|
+
this.resultsSubscription = this.resultAwareService.resultsAvailable$
|
|
18475
|
+
.pipe(filter(Boolean), take(1), timeout$1(RAIL_RESULTS_WAIT_MS), catchError$1((err) => {
|
|
18476
|
+
console.warn('[ChatbotRailSearchService] rail results timed out or errored', err);
|
|
18477
|
+
this.chatbotService.setSearching(false);
|
|
18478
|
+
return EMPTY;
|
|
18479
|
+
}))
|
|
18480
|
+
.subscribe(() => {
|
|
18481
|
+
const condensed = this.resultAwareService.getCondensedResults();
|
|
18482
|
+
const railResults = this.buildRailResults(condensed, parameters);
|
|
18483
|
+
this.chatbotService.patchAssistantMessage({ railResults, state: 'RESULTS_READY' });
|
|
18484
|
+
this.chatbotService.setSearching(false);
|
|
18485
|
+
});
|
|
18486
|
+
}
|
|
18487
|
+
buildRailResults(condensed, parameters) {
|
|
18488
|
+
const isReturn = !!parameters.returnDate;
|
|
18489
|
+
// condenseRailResults emits (cheapest, expensive) pairs per journey index.
|
|
18490
|
+
// Deduplicate outbound entries by index, keeping the first (cheapest) fare.
|
|
18491
|
+
// (Inbound legs live inside condensed[0].returnOptions and are handled
|
|
18492
|
+
// separately by buildInboundCards.)
|
|
18493
|
+
const seenOutbound = new Set();
|
|
18494
|
+
const outboundRaw = [];
|
|
18495
|
+
for (const r of condensed) {
|
|
18496
|
+
const isReturnEntry = isReturn && r.singleOrReturn?.toLowerCase().includes('return');
|
|
18497
|
+
if (isReturnEntry)
|
|
18498
|
+
continue;
|
|
18499
|
+
if (seenOutbound.has(r.index))
|
|
18500
|
+
continue;
|
|
18501
|
+
seenOutbound.add(r.index);
|
|
18502
|
+
outboundRaw.push(r);
|
|
18503
|
+
}
|
|
18504
|
+
const outboundCards = outboundRaw
|
|
18505
|
+
.slice(0, MAX_RAIL_CARDS_PER_LEG)
|
|
18506
|
+
.map((r) => this.toRailCard(r, parameters.departure, parameters.arrival, parameters.departureDate));
|
|
18507
|
+
const inboundCards = isReturn && parameters.returnDate
|
|
18508
|
+
? this.buildInboundCards(condensed, parameters, parameters.returnDate)
|
|
18509
|
+
: undefined;
|
|
18510
|
+
return { outbound: outboundCards, inbound: inboundCards, isReturn, parameters };
|
|
18511
|
+
}
|
|
18512
|
+
buildInboundCards(condensed, parameters, returnDate) {
|
|
18513
|
+
// Return journey options live inside condensed[0].returnOptions[]
|
|
18514
|
+
const returnOptions = condensed[0]?.returnOptions ?? [];
|
|
18515
|
+
const seen = new Set();
|
|
18516
|
+
const cards = [];
|
|
18517
|
+
for (const ro of returnOptions) {
|
|
18518
|
+
if (seen.has(ro.index))
|
|
18519
|
+
continue;
|
|
18520
|
+
seen.add(ro.index);
|
|
18521
|
+
const { singlePrice, singleTickets } = this.resultAwareService.getRailReturnJourneyFares(ro.index);
|
|
18522
|
+
cards.push({
|
|
18523
|
+
index: ro.index,
|
|
18524
|
+
departDate: this.formatDate(returnDate),
|
|
18525
|
+
departTime: ro.depart,
|
|
18526
|
+
arriveTime: ro.arrive,
|
|
18527
|
+
from: parameters.arrival,
|
|
18528
|
+
to: parameters.departure,
|
|
18529
|
+
route: `${parameters.arrival} → ${parameters.departure}`,
|
|
18530
|
+
duration: ro.duration,
|
|
18531
|
+
changes: ro.stops,
|
|
18532
|
+
fromPrice: ro.price,
|
|
18533
|
+
currency: 'GBP',
|
|
18534
|
+
operator: ro.operator ?? '',
|
|
18535
|
+
policyStatus: ro.policyStatus,
|
|
18536
|
+
fares: { singlePrice, currency: 'GBP', singleTickets }
|
|
18537
|
+
});
|
|
18538
|
+
if (cards.length === MAX_RAIL_CARDS_PER_LEG)
|
|
18539
|
+
break;
|
|
18540
|
+
}
|
|
18541
|
+
return cards;
|
|
18542
|
+
}
|
|
18543
|
+
toRailCard(r, from, to, date) {
|
|
18544
|
+
const { singlePrice, returnPrice, singleTickets, returnTickets } = this.resultAwareService.getRailJourneyFares(r.index);
|
|
18545
|
+
return {
|
|
18546
|
+
index: r.index,
|
|
18547
|
+
departDate: this.formatDate(date),
|
|
18548
|
+
departTime: r.depart,
|
|
18549
|
+
arriveTime: r.arrive,
|
|
18550
|
+
from,
|
|
18551
|
+
to,
|
|
18552
|
+
route: `${from} → ${to}`,
|
|
18553
|
+
duration: r.duration,
|
|
18554
|
+
changes: r.stops,
|
|
18555
|
+
fromPrice: r.price,
|
|
18556
|
+
currency: 'GBP',
|
|
18557
|
+
operator: r.operator ?? '',
|
|
18558
|
+
policyStatus: r.policyStatus,
|
|
18559
|
+
fares: { singlePrice, returnPrice, currency: 'GBP', singleTickets, returnTickets }
|
|
18560
|
+
};
|
|
18561
|
+
}
|
|
18562
|
+
formatDate(dateStr) {
|
|
18563
|
+
try {
|
|
18564
|
+
return new Date(dateStr).toLocaleDateString('en-GB', {
|
|
18565
|
+
weekday: 'short',
|
|
18566
|
+
day: 'numeric',
|
|
18567
|
+
month: 'short',
|
|
18568
|
+
year: 'numeric'
|
|
18569
|
+
});
|
|
18570
|
+
}
|
|
18571
|
+
catch {
|
|
18572
|
+
return dateStr;
|
|
18573
|
+
}
|
|
18574
|
+
}
|
|
18575
|
+
// ── Param population ─────────────────────────────────────────────────────
|
|
18576
|
+
async populateRailParams(parameters, departNgbDate) {
|
|
18577
|
+
const railParams = this.searchService.searches[ServiceType.Rail];
|
|
18578
|
+
railParams.outbound_date = departNgbDate;
|
|
18579
|
+
if (parameters.departureTime) {
|
|
18580
|
+
railParams.outbound_time = this.normalizeTime(parameters.departureTime);
|
|
18581
|
+
}
|
|
18582
|
+
if (parameters.returnDate) {
|
|
18583
|
+
const returnDate = moment$1(parameters.returnDate);
|
|
18584
|
+
railParams.return_date = {
|
|
18585
|
+
year: returnDate.year(),
|
|
18586
|
+
month: returnDate.month() + 1,
|
|
18587
|
+
day: returnDate.date()
|
|
18588
|
+
};
|
|
18589
|
+
railParams.chosenSearchType = RailSearchJourneyType.ReturnJourney;
|
|
18590
|
+
if (parameters.returnTime) {
|
|
18591
|
+
railParams.return_time = this.normalizeTime(parameters.returnTime);
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
else {
|
|
18595
|
+
railParams.chosenSearchType = RailSearchJourneyType.SingleJourney;
|
|
18596
|
+
}
|
|
18597
|
+
if (parameters.railClass) {
|
|
18598
|
+
railParams.chosenSearchClass = RailClass[parameters.railClass] ?? RailClass.All;
|
|
18599
|
+
}
|
|
18600
|
+
if (parameters.outboundTimeCriteria) {
|
|
18601
|
+
railParams.outboundCriteria =
|
|
18602
|
+
parameters.outboundTimeCriteria === 'Arrive'
|
|
18603
|
+
? RailSearchCriteria.Arrive
|
|
18604
|
+
: RailSearchCriteria.Depart;
|
|
18605
|
+
}
|
|
18606
|
+
if (parameters.returnTimeCriteria) {
|
|
18607
|
+
railParams.returnCriteria =
|
|
18608
|
+
parameters.returnTimeCriteria === 'Arrive'
|
|
18609
|
+
? RailSearchCriteria.Arrive
|
|
18610
|
+
: RailSearchCriteria.Depart;
|
|
18611
|
+
}
|
|
18612
|
+
if (parameters.isAirportExpress) {
|
|
18613
|
+
railParams.isAirportExpress = true;
|
|
18614
|
+
if (parameters.airportExpressRoute) {
|
|
18615
|
+
railParams.airportExpressStation = parameters.airportExpressRoute;
|
|
18616
|
+
}
|
|
18617
|
+
}
|
|
18618
|
+
else {
|
|
18619
|
+
railParams.isAirportExpress = false;
|
|
18620
|
+
railParams.airportExpressStation = null;
|
|
18621
|
+
}
|
|
18622
|
+
if (parameters.children) {
|
|
18623
|
+
railParams.noOfChildren = parameters.children;
|
|
18624
|
+
}
|
|
18625
|
+
// Railcards must be set BEFORE station resolution so that when locationChange
|
|
18626
|
+
// triggers loadRailcards() in the rail-search component, railParams.railCards
|
|
18627
|
+
// is already populated and selectedTravelCards gets synced correctly.
|
|
18628
|
+
if (parameters.railcards?.length) {
|
|
18629
|
+
const availableCards = await firstValueFrom(this.searchService.getRailCards('GB'));
|
|
18630
|
+
const matched = parameters.railcards
|
|
18631
|
+
.map((code) => {
|
|
18632
|
+
const upper = code.toUpperCase();
|
|
18633
|
+
return availableCards.find((c) => c.code.toUpperCase() === upper || c.name.toUpperCase().includes(upper));
|
|
18634
|
+
})
|
|
18635
|
+
.filter((c) => !!c);
|
|
18636
|
+
if (matched.length) {
|
|
18637
|
+
railParams.railCards = matched;
|
|
18638
|
+
// Prevent preselectRailcardsForTravellers from overriding the chatbot selection
|
|
18639
|
+
railParams.applyRailcards = true;
|
|
18640
|
+
}
|
|
18641
|
+
}
|
|
18642
|
+
const resolvePromises = [
|
|
18643
|
+
this.resolveRailStation(parameters.departureCode, parameters.departure),
|
|
18644
|
+
this.resolveRailStation(parameters.arrivalCode, parameters.arrival)
|
|
18645
|
+
];
|
|
18646
|
+
if (parameters.viaStation || parameters.viaCode) {
|
|
18647
|
+
resolvePromises.push(this.resolveRailStation(parameters.viaCode, parameters.viaStation || ''));
|
|
18648
|
+
}
|
|
18649
|
+
const [from, to, viaResolved] = await Promise.all(resolvePromises);
|
|
18650
|
+
if (from) {
|
|
18651
|
+
railParams.travellingFrom = from;
|
|
18652
|
+
}
|
|
18653
|
+
if (to) {
|
|
18654
|
+
railParams.travellingTo = to;
|
|
18655
|
+
}
|
|
18656
|
+
if (viaResolved) {
|
|
18657
|
+
railParams.via = true;
|
|
18658
|
+
railParams.viaLocation = viaResolved;
|
|
18659
|
+
}
|
|
18660
|
+
// Re-apply journey type after station resolution: setting travellingFrom/To
|
|
18661
|
+
// triggers validateAndPrepare() which can reset chosenSearchType from
|
|
18662
|
+
// session storage.
|
|
18663
|
+
if (parameters.returnDate) {
|
|
18664
|
+
railParams.chosenSearchType = RailSearchJourneyType.ReturnJourney;
|
|
18665
|
+
}
|
|
18666
|
+
}
|
|
18667
|
+
normalizeTime(time) {
|
|
18668
|
+
const parsed = moment$1(time, ['H:mm', 'HH:mm', 'ha', 'h:mma', 'h a', 'h:mm a']);
|
|
18669
|
+
if (parsed.isValid()) {
|
|
18670
|
+
return parsed.format('HHmm');
|
|
18671
|
+
}
|
|
18672
|
+
return time.replace(':', '');
|
|
18673
|
+
}
|
|
18674
|
+
preferRailStation(stations) {
|
|
18675
|
+
const hub = stations.find((s) => s.name && /\bhub\b/i.test(s.name));
|
|
18676
|
+
if (hub) {
|
|
18677
|
+
return hub;
|
|
18678
|
+
}
|
|
18679
|
+
const nonAirport = stations.find((s) => s.name && !s.name.toLowerCase().includes('airport'));
|
|
18680
|
+
return nonAirport || stations[0];
|
|
18681
|
+
}
|
|
18682
|
+
async resolveRailStation(code, name) {
|
|
18683
|
+
if (!name && !code) {
|
|
18684
|
+
return null;
|
|
18685
|
+
}
|
|
18686
|
+
if (code) {
|
|
18687
|
+
const byCode = await this.lookupRailStations(code);
|
|
18688
|
+
if (byCode?.length) {
|
|
18689
|
+
const exact = byCode.find((s) => s?.railstation?.stationCode === code || s?.stationCode === code);
|
|
18690
|
+
if (exact)
|
|
18691
|
+
return exact;
|
|
18692
|
+
const preferred = this.preferRailStation(byCode);
|
|
18693
|
+
if (preferred)
|
|
18694
|
+
return preferred;
|
|
18695
|
+
}
|
|
18696
|
+
}
|
|
18697
|
+
if (!name) {
|
|
18698
|
+
return null;
|
|
18699
|
+
}
|
|
18700
|
+
const candidates = this.buildRailStationNameCandidates(name);
|
|
18701
|
+
for (const candidate of candidates) {
|
|
18702
|
+
const stations = await this.lookupRailStations(candidate);
|
|
18703
|
+
if (stations?.length) {
|
|
18704
|
+
if (code) {
|
|
18705
|
+
const byCode = stations.find((s) => s?.railstation?.stationCode === code || s?.stationCode === code);
|
|
18706
|
+
if (byCode)
|
|
18707
|
+
return byCode;
|
|
18708
|
+
}
|
|
18709
|
+
const preferred = this.preferRailStation(stations);
|
|
18710
|
+
if (preferred)
|
|
18711
|
+
return preferred;
|
|
18712
|
+
}
|
|
18713
|
+
}
|
|
18714
|
+
return null;
|
|
18715
|
+
}
|
|
18716
|
+
async lookupRailStations(query) {
|
|
18717
|
+
try {
|
|
18718
|
+
const stations = await firstValueFrom(this.searchService.getStationLocations(query));
|
|
18719
|
+
return stations ?? null;
|
|
18720
|
+
}
|
|
18721
|
+
catch {
|
|
18722
|
+
return null;
|
|
18723
|
+
}
|
|
18724
|
+
}
|
|
18725
|
+
/**
|
|
18726
|
+
* Generate progressively-cleaner name variants for rail station lookup.
|
|
18727
|
+
* e.g. "Manchester (all stations)" → ["Manchester (all stations)", "Manchester", "Manchester all stations"]
|
|
18728
|
+
*/
|
|
18729
|
+
buildRailStationNameCandidates(name) {
|
|
18730
|
+
const variants = new Set();
|
|
18731
|
+
const trimmed = name.trim();
|
|
18732
|
+
if (trimmed)
|
|
18733
|
+
variants.add(trimmed);
|
|
18734
|
+
const withoutParens = trimmed
|
|
18735
|
+
.replace(/\s*\([^)]*\)\s*/g, ' ')
|
|
18736
|
+
.replace(/\s+/g, ' ')
|
|
18737
|
+
.trim();
|
|
18738
|
+
if (withoutParens)
|
|
18739
|
+
variants.add(withoutParens);
|
|
18740
|
+
const firstWord = withoutParens.split(/\s+/)[0];
|
|
18741
|
+
if (firstWord && firstWord.length > 2)
|
|
18742
|
+
variants.add(firstWord);
|
|
18743
|
+
return Array.from(variants);
|
|
18744
|
+
}
|
|
18745
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotRailSearchService, deps: [{ token: ChatbotService }, { token: EnterpriseSearchService }, { token: ResultAwareService }, { token: UserService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
18746
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotRailSearchService, providedIn: 'root' });
|
|
18747
|
+
}
|
|
18748
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotRailSearchService, decorators: [{
|
|
18749
|
+
type: Injectable,
|
|
18750
|
+
args: [{ providedIn: 'root' }]
|
|
18751
|
+
}], ctorParameters: () => [{ type: ChatbotService }, { type: EnterpriseSearchService }, { type: ResultAwareService }, { type: UserService }] });
|
|
18752
|
+
|
|
18753
|
+
/**
|
|
18754
|
+
* Extracts journey itineraries from a `FlightQuoteResultArray`. Returns both
|
|
18755
|
+
* outbound and (when present) inbound legs separately so the chat UI can
|
|
18756
|
+
* render two card stacks for dual-single return searches.
|
|
18757
|
+
*
|
|
18758
|
+
* `journeys` (return-paired) itineraries carry both legs inside each entry,
|
|
18759
|
+
* so they're returned in `outbound` only.
|
|
18760
|
+
* `multi` (dual-singles) splits the legs across two arrays, which we surface
|
|
18761
|
+
* separately.
|
|
18762
|
+
*/
|
|
18763
|
+
function extractJourneys(results) {
|
|
18764
|
+
const empty = { outbound: [], inbound: [], multi: [] };
|
|
18765
|
+
if (!results)
|
|
18766
|
+
return empty;
|
|
18767
|
+
const bundles = Array.isArray(results) ? results : [results];
|
|
18768
|
+
const out = { outbound: [], inbound: [], multi: [] };
|
|
18769
|
+
for (const bundle of bundles) {
|
|
18770
|
+
if (!bundle)
|
|
18771
|
+
continue;
|
|
18772
|
+
if (Array.isArray(bundle.journeys) && bundle.journeys.length) {
|
|
18773
|
+
out.outbound.push(...bundle.journeys);
|
|
18774
|
+
}
|
|
18775
|
+
else if (Array.isArray(bundle.multi)) {
|
|
18776
|
+
// Per-leg arrays — for multi-city this can be 3+ entries. We retain the
|
|
18777
|
+
// full N-leg shape so the multi-city path can render every segment,
|
|
18778
|
+
// while keeping the legacy dual-singles fields populated for the
|
|
18779
|
+
// existing return-as-two-singles UI.
|
|
18780
|
+
const perLeg = bundle.multi.map((m) => Array.isArray(m?.results) ? m.results : []);
|
|
18781
|
+
if (perLeg.length >= 2) {
|
|
18782
|
+
out.multi.push(perLeg);
|
|
18783
|
+
}
|
|
18784
|
+
const outboundLeg = perLeg[0];
|
|
18785
|
+
const inboundLeg = perLeg[1];
|
|
18786
|
+
if (Array.isArray(outboundLeg))
|
|
18787
|
+
out.outbound.push(...outboundLeg);
|
|
18788
|
+
if (Array.isArray(inboundLeg) && perLeg.length === 2)
|
|
18789
|
+
out.inbound.push(...inboundLeg);
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
return out;
|
|
18793
|
+
}
|
|
18794
|
+
const FLIGHT_RESULTS_WAIT_MS = 90_000;
|
|
18795
|
+
const MAX_FLIGHT_CARDS = 3;
|
|
18796
|
+
/**
|
|
18797
|
+
* Handles chatbot-driven flight searches. Registered with
|
|
18798
|
+
* `ChatbotSearchDispatcherService`; not used directly.
|
|
18799
|
+
*
|
|
18800
|
+
* Populates the flight search state from the chatbot's parameters (same state
|
|
18801
|
+
* the search form binds to via ngModel) and runs `startSearch()` headlessly
|
|
18802
|
+
* so chatbot flight searches work from any page. Once results arrive, builds
|
|
18803
|
+
* `ChatFlightResultCard`s and patches them onto the last assistant message
|
|
18804
|
+
* via {@link ChatbotService.patchAssistantMessage}.
|
|
18805
|
+
*/
|
|
18806
|
+
class ChatbotFlightSearchService {
|
|
18807
|
+
chatbotService;
|
|
18808
|
+
searchService;
|
|
18809
|
+
resultAwareService;
|
|
18810
|
+
userService;
|
|
18811
|
+
travelType = 'flight';
|
|
18812
|
+
resultsSubscription = null;
|
|
18813
|
+
constructor(chatbotService, searchService, resultAwareService, userService) {
|
|
18814
|
+
this.chatbotService = chatbotService;
|
|
18815
|
+
this.searchService = searchService;
|
|
18816
|
+
this.resultAwareService = resultAwareService;
|
|
18817
|
+
this.userService = userService;
|
|
18818
|
+
}
|
|
18819
|
+
async handle(parameters, autoSearch) {
|
|
18820
|
+
this.searchService.resetSearchChosenObjects();
|
|
18821
|
+
this.searchService.resetSearchPriorities();
|
|
18822
|
+
this.searchService.search_objects[ServiceType.Flight].chosen = true;
|
|
18823
|
+
this.searchService.search_objects[ServiceType.Flight].originalChosen = true;
|
|
18824
|
+
this.searchService.determineHighestSearchPriority();
|
|
18825
|
+
if (this.searchService.travellerInformation.value.length === 0) {
|
|
18826
|
+
const selfTraveller = this.userService.createSelfTraveller();
|
|
18827
|
+
if (selfTraveller) {
|
|
18828
|
+
this.searchService.addTraveller(selfTraveller);
|
|
18829
|
+
}
|
|
18830
|
+
}
|
|
18831
|
+
const isMultiCity = (parameters.legs?.length ?? 0) >= 2;
|
|
18832
|
+
// For multi-city the reference date for any downstream calendar widgets
|
|
18833
|
+
// is the first leg; for single/return it's the top-level departureDate.
|
|
18834
|
+
const refDateStr = isMultiCity ? parameters.legs[0].date : parameters.departureDate;
|
|
18835
|
+
const refDate = moment$1(refDateStr);
|
|
18836
|
+
const departNgbDate = {
|
|
18837
|
+
year: refDate.year(),
|
|
18838
|
+
month: refDate.month() + 1,
|
|
18839
|
+
day: refDate.date()
|
|
18840
|
+
};
|
|
18841
|
+
if (autoSearch) {
|
|
18842
|
+
this.subscribeToFlightResults(parameters);
|
|
18843
|
+
}
|
|
18844
|
+
if (isMultiCity) {
|
|
18845
|
+
await this.populateMultiCityFlightParams(parameters);
|
|
18846
|
+
}
|
|
18847
|
+
else {
|
|
18848
|
+
await this.populateFlightParams(parameters, departNgbDate);
|
|
18849
|
+
}
|
|
18850
|
+
if (!autoSearch) {
|
|
18851
|
+
return;
|
|
18852
|
+
}
|
|
18853
|
+
const flightSearch = this.searchService.searches[ServiceType.Flight];
|
|
18854
|
+
// Surface OBT's existing validation (past dates, same origin/destination,
|
|
18855
|
+
// missing airports, return-before-depart, etc.) instead of letting the
|
|
18856
|
+
// user wait for a search that will silently no-op. Setters on
|
|
18857
|
+
// FlightEnterpriseSearch run `_isValid()` on every assignment, so the
|
|
18858
|
+
// array is already populated by the time `populateFlightParams` returns.
|
|
18859
|
+
const validationMessages = Array.isArray(flightSearch.validationMessages)
|
|
18860
|
+
? flightSearch.validationMessages.filter(Boolean)
|
|
18861
|
+
: [];
|
|
18862
|
+
if (validationMessages.length) {
|
|
18863
|
+
this.resultsSubscription?.unsubscribe();
|
|
18864
|
+
this.resultsSubscription = null;
|
|
18865
|
+
this.chatbotService.patchAssistantMessage({
|
|
18866
|
+
text: this.formatValidationMessages(validationMessages),
|
|
18867
|
+
state: 'RESULTS_READY'
|
|
18868
|
+
});
|
|
18869
|
+
this.chatbotService.setSearching(false);
|
|
18870
|
+
return;
|
|
18871
|
+
}
|
|
18872
|
+
const started = await flightSearch.startSearch();
|
|
18873
|
+
if (!started) {
|
|
18874
|
+
// Safety net — should rarely fire now that we read validationMessages
|
|
18875
|
+
// up-front, but kept in case startSearch rejects for another reason.
|
|
18876
|
+
this.resultsSubscription?.unsubscribe();
|
|
18877
|
+
this.resultsSubscription = null;
|
|
18878
|
+
const messages = Array.isArray(flightSearch.validationMessages)
|
|
18879
|
+
? flightSearch.validationMessages.filter(Boolean)
|
|
18880
|
+
: [];
|
|
18881
|
+
this.chatbotService.patchAssistantMessage({
|
|
18882
|
+
text: messages.length
|
|
18883
|
+
? this.formatValidationMessages(messages)
|
|
18884
|
+
: `I couldn't run that search — please check the dates and airports and try again.`,
|
|
18885
|
+
state: 'RESULTS_READY'
|
|
18886
|
+
});
|
|
18887
|
+
this.chatbotService.setSearching(false);
|
|
18888
|
+
return;
|
|
18889
|
+
}
|
|
18890
|
+
// Note: result capture happens inside `subscribeToFlightResults`; we don't
|
|
18891
|
+
// read `flightSearch.results` here because it's a BehaviorSubject that may
|
|
18892
|
+
// still be empty at this point.
|
|
18893
|
+
}
|
|
18894
|
+
// ── Result capture + card building ───────────────────────────────────────
|
|
18895
|
+
subscribeToFlightResults(parameters) {
|
|
18896
|
+
this.resultsSubscription?.unsubscribe();
|
|
18897
|
+
// Reset the BehaviorSubject so a second search doesn't fire the take(1)
|
|
18898
|
+
// subscriber immediately with stale data and then never update.
|
|
18899
|
+
this.resultAwareService.clearResults();
|
|
18900
|
+
this.chatbotService.setSearching(true, 'flight');
|
|
18901
|
+
// Subscribe directly to the flight search's results BehaviorSubject. Unlike
|
|
18902
|
+
// rail, flight `startSearch()` can resolve before results stream in, so
|
|
18903
|
+
// reading `results.value` after the await isn't reliable. We wait for the
|
|
18904
|
+
// first emission that contains actual journey itineraries.
|
|
18905
|
+
const flightSearch = this.searchService.searches[ServiceType.Flight];
|
|
18906
|
+
// Don't mutate the shared `results` BehaviorSubject here: other views
|
|
18907
|
+
// (e.g. the flight results page) subscribe to the same singleton and
|
|
18908
|
+
// pushing `[]` would briefly flash them an empty state. The new
|
|
18909
|
+
// `startSearch()` below replaces `results.value` before `isLoading`
|
|
18910
|
+
// flips to false, so the value we read in the completion handler is
|
|
18911
|
+
// always the fresh search's output.
|
|
18912
|
+
// Use `isLoading` (true → false transition) as the search-complete signal
|
|
18913
|
+
// rather than waiting on the results subject. The previous filter required
|
|
18914
|
+
// `r.length > 0 && hasJourneys(...)` — but a genuinely empty response
|
|
18915
|
+
// (e.g. no Premium Economy KLM flights on that date) also produces an
|
|
18916
|
+
// empty array, indistinguishable from "haven't searched yet", so the
|
|
18917
|
+
// subscriber would hang until the 90s timeout instead of rendering the
|
|
18918
|
+
// "No flights found" empty state. `isLoading` reliably flips to false in
|
|
18919
|
+
// `_startSearch`'s complete/error handlers either way.
|
|
18920
|
+
const loadingSubject = flightSearch.isLoading;
|
|
18921
|
+
this.resultsSubscription = loadingSubject
|
|
18922
|
+
.pipe(skip(1), // drop the BehaviorSubject's replay of the current value
|
|
18923
|
+
filter((loading) => loading === false), take(1), timeout$1(FLIGHT_RESULTS_WAIT_MS), catchError$1((err) => {
|
|
18924
|
+
console.warn('[ChatbotFlightSearchService] flight results timed out or errored', err);
|
|
18925
|
+
this.chatbotService.patchAssistantMessage({
|
|
18926
|
+
flightResults: {
|
|
18927
|
+
outbound: [],
|
|
18928
|
+
isReturn: !!parameters.returnDate,
|
|
18929
|
+
isMultiCity: (parameters.legs?.length ?? 0) >= 2,
|
|
18930
|
+
parameters,
|
|
18931
|
+
sort: 'cheapest'
|
|
18932
|
+
},
|
|
18933
|
+
state: 'RESULTS_READY'
|
|
18934
|
+
});
|
|
18935
|
+
this.chatbotService.setSearching(false);
|
|
18936
|
+
return EMPTY;
|
|
18937
|
+
}))
|
|
18938
|
+
.subscribe(() => {
|
|
18939
|
+
const rawResults = flightSearch.results?.value ?? [];
|
|
18940
|
+
const { outbound, inbound, multi } = extractJourneys(rawResults);
|
|
18941
|
+
const isMultiCity = (parameters.legs?.length ?? 0) >= 2;
|
|
18942
|
+
// Feed ResultAwareService with outbound so its condensed-cards API is
|
|
18943
|
+
// populated for downstream card click → filter navigation.
|
|
18944
|
+
this.resultAwareService.captureFlightResults(outbound);
|
|
18945
|
+
const outboundCondensed = this.resultAwareService.getCondensedResults();
|
|
18946
|
+
let inboundCondensed = [];
|
|
18947
|
+
if (inbound.length) {
|
|
18948
|
+
// Build inbound condensed by re-running the condensation. The
|
|
18949
|
+
// ResultAwareService keeps state for outbound, so we condense the
|
|
18950
|
+
// raw inbound array via a temporary capture/restore.
|
|
18951
|
+
this.resultAwareService.captureFlightResults(inbound);
|
|
18952
|
+
inboundCondensed = this.resultAwareService.getCondensedResults();
|
|
18953
|
+
// Restore outbound as the "current" capture for click-through filters.
|
|
18954
|
+
this.resultAwareService.captureFlightResults(outbound);
|
|
18955
|
+
}
|
|
18956
|
+
const flightResults = isMultiCity
|
|
18957
|
+
? this.buildMultiCityFlightResults(parameters, outbound)
|
|
18958
|
+
: this.buildFlightResults(outboundCondensed, inboundCondensed, parameters, outbound, inbound);
|
|
18959
|
+
this.chatbotService.patchAssistantMessage({
|
|
18960
|
+
flightResults,
|
|
18961
|
+
state: 'RESULTS_READY'
|
|
18962
|
+
});
|
|
18963
|
+
this.chatbotService.setSearching(false);
|
|
18964
|
+
});
|
|
18965
|
+
}
|
|
18966
|
+
buildFlightResults(outbound, inbound, parameters, rawOutbound = [], rawInbound = []) {
|
|
18967
|
+
const isReturn = !!parameters.returnDate;
|
|
18968
|
+
// sortType is widened on ChatSearchParameters to cover hotel + flight values;
|
|
18969
|
+
// narrow back to the flight-only subset accepted by ChatFlightResults.sort.
|
|
18970
|
+
const sort = parameters.sortType === 'fastest' || parameters.sortType === 'shortest'
|
|
18971
|
+
? parameters.sortType
|
|
18972
|
+
: 'cheapest';
|
|
18973
|
+
// Optional airline filter: if the user asked for a specific carrier
|
|
18974
|
+
// ("Give me BA flights…"), keep only matching itineraries. Honour it
|
|
18975
|
+
// strictly — better to show "no matches" than mislead the user.
|
|
18976
|
+
const airlineFilter = (parameters.airlineCode || parameters.airline || '').trim().toLowerCase();
|
|
18977
|
+
const applyAirline = (list) => {
|
|
18978
|
+
if (!airlineFilter)
|
|
18979
|
+
return list;
|
|
18980
|
+
return list.filter((r) => {
|
|
18981
|
+
const code = (r.flightNumbers?.match(/^[A-Z]{2,3}/i)?.[0] || '').toLowerCase();
|
|
18982
|
+
const name = (r.airline || '').toLowerCase();
|
|
18983
|
+
return code === airlineFilter || name.includes(airlineFilter);
|
|
18984
|
+
});
|
|
18985
|
+
};
|
|
18986
|
+
// Honour "direct only" / maxConnections=0 strictly. The form-level
|
|
18987
|
+
// maxConnections setting goes to the supplier, but some suppliers ignore
|
|
18988
|
+
// it — re-filter client-side so the user gets what they asked for.
|
|
18989
|
+
const applyStops = (list) => {
|
|
18990
|
+
if (parameters.maxConnections == null)
|
|
18991
|
+
return list;
|
|
18992
|
+
const max = parameters.maxConnections;
|
|
18993
|
+
return list.filter((r) => (r.stops ?? 0) <= max);
|
|
18994
|
+
};
|
|
18995
|
+
const outFiltered = applyStops(applyAirline(outbound));
|
|
18996
|
+
const inFiltered = applyStops(applyAirline(inbound));
|
|
18997
|
+
// Dedupe by physical-flight signature, keep the cheapest fare per flight,
|
|
18998
|
+
// then sort. Without dedup we'd often render two or three "cards" that
|
|
18999
|
+
// are the same KL1034 with different fare brands (Restricted/Flex/Plus)
|
|
19000
|
+
// — the user wants one card per flight with the cheapest price shown,
|
|
19001
|
+
// and the existing fare-options expand panel (built via
|
|
19002
|
+
// indexFaresByFlights) surfaces the other bookable fares.
|
|
19003
|
+
//
|
|
19004
|
+
// Key includes outbound flight numbers + inbound flight numbers AND each
|
|
19005
|
+
// leg's depart time, so two operationally-distinct flights that happen
|
|
19006
|
+
// to share marketing flight numbers (codeshares on different equipment
|
|
19007
|
+
// or schedules) don't collapse into one card.
|
|
19008
|
+
const parseDurationToMins = (d) => {
|
|
19009
|
+
if (!d)
|
|
19010
|
+
return Number.POSITIVE_INFINITY;
|
|
19011
|
+
const h = /(\d+)\s*h/i.exec(d);
|
|
19012
|
+
const m = /(\d+)\s*m/i.exec(d);
|
|
19013
|
+
const total = (h ? +h[1] : 0) * 60 + (m ? +m[1] : 0);
|
|
19014
|
+
return total > 0 ? total : Number.POSITIVE_INFINITY;
|
|
19015
|
+
};
|
|
19016
|
+
const totalDurationMins = (r) => parseDurationToMins(r.duration) + (r.inbound ? parseDurationToMins(r.inbound.duration) : 0);
|
|
19017
|
+
const dedupeCheapestByFlight = (list) => {
|
|
19018
|
+
const seen = new Map();
|
|
19019
|
+
for (const r of list) {
|
|
19020
|
+
const out = r.flightNumbers || '';
|
|
19021
|
+
const inb = r.inbound?.flightNumbers || '';
|
|
19022
|
+
if (!out && !inb) {
|
|
19023
|
+
// No flight numbers to key on (shouldn't happen for real results);
|
|
19024
|
+
// fall back to per-index so we don't collapse unrelated cards.
|
|
19025
|
+
seen.set(`__idx${r.index}`, r);
|
|
19026
|
+
continue;
|
|
19027
|
+
}
|
|
19028
|
+
const key = `${out}|${inb}|${r.depart}|${r.inbound?.depart ?? ''}`;
|
|
19029
|
+
const existing = seen.get(key);
|
|
19030
|
+
if (!existing || (r.price ?? Infinity) < (existing.price ?? Infinity)) {
|
|
19031
|
+
seen.set(key, r);
|
|
19032
|
+
}
|
|
19033
|
+
}
|
|
19034
|
+
const arr = Array.from(seen.values());
|
|
19035
|
+
if (sort === 'fastest' || sort === 'shortest') {
|
|
19036
|
+
arr.sort((a, b) => totalDurationMins(a) - totalDurationMins(b));
|
|
19037
|
+
}
|
|
19038
|
+
else {
|
|
19039
|
+
arr.sort((a, b) => (a.price ?? Infinity) - (b.price ?? Infinity));
|
|
19040
|
+
}
|
|
19041
|
+
return arr;
|
|
19042
|
+
};
|
|
19043
|
+
const outDeduped = dedupeCheapestByFlight(outFiltered);
|
|
19044
|
+
const inDeduped = dedupeCheapestByFlight(inFiltered);
|
|
19045
|
+
// Pre-compute fare-options groupings for the expand panel: group the raw
|
|
19046
|
+
// itineraries by their outbound (+ inbound) flight-numbers signature so a
|
|
19047
|
+
// card can show all bookable cabins/brands for the same physical flights.
|
|
19048
|
+
// For one-way searches the lookup key on the card side is just the
|
|
19049
|
+
// outbound flight numbers (no trailing "|"); use the same shape here so
|
|
19050
|
+
// the maps actually match. For returns, include the inbound legs.
|
|
19051
|
+
const outFareIndex = this.indexFaresByFlights(rawOutbound, isReturn);
|
|
19052
|
+
const inFareIndex = this.indexFaresByFlights(rawInbound, false);
|
|
19053
|
+
const outboundCards = outDeduped
|
|
19054
|
+
.slice(0, MAX_FLIGHT_CARDS)
|
|
19055
|
+
.map((r) => this.toFlightCard(r, parameters, 'outbound', outFareIndex));
|
|
19056
|
+
const inboundCards = inDeduped
|
|
19057
|
+
.slice(0, MAX_FLIGHT_CARDS)
|
|
19058
|
+
.map((r) => this.toFlightCard(r, parameters, 'inbound', inFareIndex));
|
|
19059
|
+
return { outbound: outboundCards, inbound: inboundCards, isReturn, parameters, sort };
|
|
19060
|
+
}
|
|
19061
|
+
/**
|
|
19062
|
+
* Builds a map keyed by an itinerary's flight-number signature to all
|
|
19063
|
+
* bookable fare variants (cabin/brand/price) for the same physical flights.
|
|
19064
|
+
* For combined-return journeys the key includes both outbound + inbound
|
|
19065
|
+
* flight numbers; for dual-single legs it's just that leg's numbers.
|
|
19066
|
+
*/
|
|
19067
|
+
indexFaresByFlights(raw, includeInbound) {
|
|
19068
|
+
const map = new Map();
|
|
19069
|
+
if (!raw?.length)
|
|
19070
|
+
return map;
|
|
19071
|
+
raw.forEach((it, idx) => {
|
|
19072
|
+
const key = this.flightSignature(it, includeInbound);
|
|
19073
|
+
if (!key)
|
|
19074
|
+
return;
|
|
19075
|
+
const fare = this.toFareTicket(it, idx);
|
|
19076
|
+
if (!fare)
|
|
19077
|
+
return;
|
|
19078
|
+
const arr = map.get(key) ?? [];
|
|
19079
|
+
arr.push(fare);
|
|
19080
|
+
map.set(key, arr);
|
|
19081
|
+
});
|
|
19082
|
+
// Sort each group cheapest first.
|
|
19083
|
+
for (const arr of map.values()) {
|
|
19084
|
+
arr.sort((a, b) => a.price - b.price);
|
|
19085
|
+
}
|
|
19086
|
+
return map;
|
|
19087
|
+
}
|
|
19088
|
+
flightSignature(it, includeInbound) {
|
|
19089
|
+
const out = (it.outboundFlights || it.outlegs || [])
|
|
19090
|
+
.map((l) => `${l.marketingCarrier || ''}${l.marketingFlightNumber || ''}`)
|
|
19091
|
+
.join('/');
|
|
19092
|
+
if (!out)
|
|
19093
|
+
return '';
|
|
19094
|
+
if (!includeInbound)
|
|
19095
|
+
return out;
|
|
19096
|
+
const inn = (it.inboundFlights || it.inlegs || [])
|
|
19097
|
+
.map((l) => `${l.marketingCarrier || ''}${l.marketingFlightNumber || ''}`)
|
|
19098
|
+
.join('/');
|
|
19099
|
+
return `${out}|${inn}`;
|
|
19100
|
+
}
|
|
19101
|
+
toFareTicket(it, idx) {
|
|
19102
|
+
const price = it?.total?.price;
|
|
19103
|
+
if (!(price > 0))
|
|
19104
|
+
return null;
|
|
19105
|
+
const cabin = it?.outboundFlights?.[0]?.cabinClass || it?.outlegs?.[0]?.cabinClass || '';
|
|
19106
|
+
// Fare brand can live in several places depending on supplier/source.
|
|
19107
|
+
// `fareConditions.brandName/fareFamily` is one path; `outboundFlights[0]
|
|
19108
|
+
// .additional.fareBrand.name` is what the full-results upsell dialog
|
|
19109
|
+
// reads. Fall back across both so we surface the friendliest label.
|
|
19110
|
+
const brand = it?.fareConditions?.brandName ||
|
|
19111
|
+
it?.fareConditions?.fareFamily ||
|
|
19112
|
+
it?.outboundFlights?.[0]?.additional?.fareBrand?.name ||
|
|
19113
|
+
'';
|
|
19114
|
+
const type = brand && cabin
|
|
19115
|
+
? `${this.formatCabinLabel(cabin)} – ${brand}`
|
|
19116
|
+
: brand || this.formatCabinLabel(cabin) || 'Standard fare';
|
|
19117
|
+
return {
|
|
19118
|
+
type,
|
|
19119
|
+
cabin: cabin || undefined,
|
|
19120
|
+
brand: brand || undefined,
|
|
19121
|
+
price,
|
|
19122
|
+
currency: it?.total?.currency || 'GBP',
|
|
19123
|
+
journeyIndex: idx
|
|
19124
|
+
};
|
|
19125
|
+
}
|
|
19126
|
+
/**
|
|
19127
|
+
* Builds chat cards for a multi-city itinerary. OBT returns one bundle per
|
|
19128
|
+
* search with per-leg result arrays; we surface the top N itineraries by
|
|
19129
|
+
* pairing the i-th cheapest option from every leg into a single "stacked"
|
|
19130
|
+
* card. This is naive (it doesn't try every combinatorial combination) but
|
|
19131
|
+
* gives the user a useful headline view; View Full Results owns the proper
|
|
19132
|
+
* fare-combination picker.
|
|
19133
|
+
*
|
|
19134
|
+
* OBT returns multi-city itineraries as a single `bundle.journeys[]` where
|
|
19135
|
+
* each entry has all legs concatenated in `outboundFlights[]`. We split each
|
|
19136
|
+
* itinerary by leg-date to produce N `ChatFlightLeg`s per card.
|
|
19137
|
+
*/
|
|
19138
|
+
buildMultiCityFlightResults(parameters, journeys) {
|
|
19139
|
+
const baseResults = {
|
|
19140
|
+
outbound: [],
|
|
19141
|
+
isReturn: false,
|
|
19142
|
+
isMultiCity: true,
|
|
19143
|
+
parameters,
|
|
19144
|
+
sort: 'cheapest'
|
|
19145
|
+
};
|
|
19146
|
+
const legs = parameters.legs ?? [];
|
|
19147
|
+
if (!journeys.length || !legs.length) {
|
|
19148
|
+
return baseResults;
|
|
19149
|
+
}
|
|
19150
|
+
// Dedupe by full-itinerary flight signature and sort cheapest first.
|
|
19151
|
+
const bySig = new Map();
|
|
19152
|
+
for (const j of journeys) {
|
|
19153
|
+
const sig = (j?.outboundFlights ?? [])
|
|
19154
|
+
.map((f) => `${f.marketingCarrier || ''}${f.marketingFlightNumber || ''}`)
|
|
19155
|
+
.join('/');
|
|
19156
|
+
const existing = bySig.get(sig);
|
|
19157
|
+
if (!existing || (j?.total?.price ?? Infinity) < (existing?.total?.price ?? Infinity)) {
|
|
19158
|
+
bySig.set(sig, j);
|
|
19159
|
+
}
|
|
19160
|
+
}
|
|
19161
|
+
const sorted = [...bySig.values()].sort((a, b) => (a?.total?.price ?? Infinity) - (b?.total?.price ?? Infinity));
|
|
19162
|
+
const cards = [];
|
|
19163
|
+
for (let i = 0; i < Math.min(MAX_FLIGHT_CARDS, sorted.length); i++) {
|
|
19164
|
+
const journey = sorted[i];
|
|
19165
|
+
const buckets = this.splitFlightsByLeg(journey?.outboundFlights ?? [], legs);
|
|
19166
|
+
const chatLegs = legs.map((leg, idx) => this.flightsToChatLeg(buckets[idx] ?? [], leg));
|
|
19167
|
+
const airlineCode = (journey?.outboundFlights?.[0]?.marketingCarrier || '').toUpperCase();
|
|
19168
|
+
const airline = journey?.outboundFlights?.[0]?.marketingCarrierName ||
|
|
19169
|
+
journey?.outboundFlights?.[0]?.airline ||
|
|
19170
|
+
'';
|
|
19171
|
+
const cabin = journey?.fareConditions?.outboundFareConditions?.cabinClass || '';
|
|
19172
|
+
cards.push({
|
|
19173
|
+
index: i,
|
|
19174
|
+
airline,
|
|
19175
|
+
airlineCode,
|
|
19176
|
+
outbound: chatLegs[0],
|
|
19177
|
+
legs: chatLegs,
|
|
19178
|
+
fromPrice: journey?.total?.price ?? 0,
|
|
19179
|
+
currency: journey?.total?.currency || 'GBP',
|
|
19180
|
+
cabin: this.formatCabinLabel(cabin),
|
|
19181
|
+
policyStatus: journey?.preferenceLevel === 'IN_POLICY' ? 'IN_POLICY' : undefined
|
|
19182
|
+
});
|
|
19183
|
+
}
|
|
19184
|
+
return { ...baseResults, outbound: cards };
|
|
19185
|
+
}
|
|
19186
|
+
/**
|
|
19187
|
+
* Split a concatenated multi-city flight list into per-leg buckets. Primary
|
|
19188
|
+
* key is the flight's depart date matching a leg date; falls back to the
|
|
19189
|
+
* latest leg whose date is on or before the flight (covers overnight
|
|
19190
|
+
* connections that depart the day before the next leg's planned date).
|
|
19191
|
+
*/
|
|
19192
|
+
splitFlightsByLeg(flights, legs) {
|
|
19193
|
+
const buckets = legs.map(() => []);
|
|
19194
|
+
let currentLeg = 0;
|
|
19195
|
+
for (const f of flights) {
|
|
19196
|
+
const fDate = (f?.departureDateTime || '').slice(0, 10);
|
|
19197
|
+
// Advance currentLeg while the leg's date is strictly before this
|
|
19198
|
+
// flight's date — preserves ordering even when a leg connects overnight.
|
|
19199
|
+
while (currentLeg + 1 < legs.length && legs[currentLeg + 1].date <= fDate) {
|
|
19200
|
+
currentLeg++;
|
|
19201
|
+
}
|
|
19202
|
+
buckets[currentLeg].push(f);
|
|
19203
|
+
}
|
|
19204
|
+
return buckets;
|
|
19205
|
+
}
|
|
19206
|
+
/** Build a `ChatFlightLeg` from the flights assigned to a single leg. */
|
|
19207
|
+
flightsToChatLeg(flights, leg) {
|
|
19208
|
+
const first = flights[0] ?? {};
|
|
19209
|
+
const last = flights[flights.length - 1] ?? first;
|
|
19210
|
+
const flightNumbers = flights
|
|
19211
|
+
.map((f) => `${f.marketingCarrier || ''}${f.marketingFlightNumber || ''}`)
|
|
19212
|
+
.filter(Boolean)
|
|
19213
|
+
.join('/');
|
|
19214
|
+
let duration = '';
|
|
19215
|
+
if (first.departureDateTime && last.arrivalDateTime) {
|
|
19216
|
+
const mins = Math.round((new Date(last.arrivalDateTime).getTime() - new Date(first.departureDateTime).getTime()) /
|
|
19217
|
+
60000);
|
|
19218
|
+
duration = this.formatDuration(mins);
|
|
19219
|
+
}
|
|
19220
|
+
return {
|
|
19221
|
+
fromCode: first.departureAirport || leg.departureCode || leg.departure,
|
|
19222
|
+
fromName: leg.departure,
|
|
19223
|
+
toCode: last.arrivalAirport || leg.arrivalCode || leg.arrival,
|
|
19224
|
+
toName: leg.arrival,
|
|
19225
|
+
departDate: this.formatDate(leg.date),
|
|
19226
|
+
departTime: this.formatHHmm(first.departureDateTime),
|
|
19227
|
+
arriveTime: this.formatHHmm(last.arrivalDateTime),
|
|
19228
|
+
duration,
|
|
19229
|
+
stops: Math.max(0, flights.length - 1),
|
|
19230
|
+
flightNumbers
|
|
19231
|
+
};
|
|
19232
|
+
}
|
|
19233
|
+
formatHHmm(iso) {
|
|
19234
|
+
if (!iso)
|
|
19235
|
+
return '';
|
|
19236
|
+
const d = new Date(iso);
|
|
19237
|
+
if (Number.isNaN(d.getTime()))
|
|
19238
|
+
return '';
|
|
19239
|
+
return d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
19240
|
+
}
|
|
19241
|
+
formatDuration(minutes) {
|
|
19242
|
+
if (!minutes || minutes <= 0)
|
|
19243
|
+
return '';
|
|
19244
|
+
const h = Math.floor(minutes / 60);
|
|
19245
|
+
const m = minutes % 60;
|
|
19246
|
+
return h ? `${h}h ${m}m` : `${m}m`;
|
|
19247
|
+
}
|
|
19248
|
+
toFlightCard(r, parameters, direction = 'outbound', fareIndex) {
|
|
19249
|
+
const airlineCode = (r.flightNumbers?.match(/^[A-Z]{2,3}/i)?.[0] || '').toUpperCase();
|
|
19250
|
+
// For dual-singles inbound cards the leg geography is reversed.
|
|
19251
|
+
const fromCode = direction === 'inbound'
|
|
19252
|
+
? r.from || parameters.arrivalCode || parameters.arrival
|
|
19253
|
+
: r.from || parameters.departureCode || parameters.departure;
|
|
19254
|
+
const toCode = direction === 'inbound'
|
|
19255
|
+
? r.to || parameters.departureCode || parameters.departure
|
|
19256
|
+
: r.to || parameters.arrivalCode || parameters.arrival;
|
|
19257
|
+
const fromName = direction === 'inbound' ? parameters.arrival : parameters.departure;
|
|
19258
|
+
const toName = direction === 'inbound' ? parameters.departure : parameters.arrival;
|
|
19259
|
+
const departDateRaw = direction === 'inbound'
|
|
19260
|
+
? (parameters.returnDate ?? parameters.departureDate)
|
|
19261
|
+
: parameters.departureDate;
|
|
19262
|
+
const outboundLeg = {
|
|
19263
|
+
fromCode,
|
|
19264
|
+
fromName,
|
|
19265
|
+
toCode,
|
|
19266
|
+
toName,
|
|
19267
|
+
departDate: this.formatDate(departDateRaw),
|
|
19268
|
+
departTime: r.depart,
|
|
19269
|
+
arriveTime: r.arrive,
|
|
19270
|
+
duration: r.duration,
|
|
19271
|
+
stops: r.stops,
|
|
19272
|
+
flightNumbers: r.flightNumbers ?? ''
|
|
19273
|
+
};
|
|
19274
|
+
// For combined-return itineraries (journeys), each result already has
|
|
19275
|
+
// both legs inside `r.inbound`. We only attach this for outbound cards.
|
|
19276
|
+
const inbound = direction === 'outbound' && r.inbound && parameters.returnDate
|
|
19277
|
+
? {
|
|
19278
|
+
fromCode: r.to || parameters.arrivalCode || parameters.arrival,
|
|
19279
|
+
fromName: parameters.arrival,
|
|
19280
|
+
toCode: r.from || parameters.departureCode || parameters.departure,
|
|
19281
|
+
toName: parameters.departure,
|
|
19282
|
+
departDate: this.formatDate(parameters.returnDate),
|
|
19283
|
+
departTime: r.inbound.depart,
|
|
19284
|
+
arriveTime: r.inbound.arrive,
|
|
19285
|
+
duration: r.inbound.duration,
|
|
19286
|
+
stops: r.inbound.stops,
|
|
19287
|
+
flightNumbers: r.inbound.flightNumbers ?? ''
|
|
19288
|
+
}
|
|
19289
|
+
: undefined;
|
|
19290
|
+
// Look up the fare-options group (same physical flights, different
|
|
19291
|
+
// cabin/brand variants). For combined-return cards the key is "out|in";
|
|
19292
|
+
// for outbound-only / inbound-only legs it's just that leg's numbers.
|
|
19293
|
+
let fares;
|
|
19294
|
+
if (fareIndex) {
|
|
19295
|
+
const key = inbound
|
|
19296
|
+
? `${r.flightNumbers ?? ''}|${r.inbound?.flightNumbers ?? ''}`
|
|
19297
|
+
: (r.flightNumbers ?? '');
|
|
19298
|
+
const tickets = fareIndex.get(key);
|
|
19299
|
+
if (tickets && tickets.length) {
|
|
19300
|
+
fares = { tickets, currency: tickets[0].currency };
|
|
19301
|
+
}
|
|
19302
|
+
}
|
|
19303
|
+
return {
|
|
19304
|
+
index: r.index,
|
|
19305
|
+
airline: r.airline ?? '',
|
|
19306
|
+
airlineCode,
|
|
19307
|
+
outbound: outboundLeg,
|
|
19308
|
+
inbound,
|
|
19309
|
+
fromPrice: r.price,
|
|
19310
|
+
currency: 'GBP',
|
|
19311
|
+
cabin: this.formatCabinLabel(r.cabin ?? parameters.cabinClass ?? ''),
|
|
19312
|
+
policyStatus: r.policyStatus,
|
|
19313
|
+
fares
|
|
19314
|
+
};
|
|
19315
|
+
}
|
|
19316
|
+
formatDate(dateStr) {
|
|
19317
|
+
try {
|
|
19318
|
+
return new Date(dateStr).toLocaleDateString('en-GB', {
|
|
19319
|
+
weekday: 'short',
|
|
19320
|
+
day: 'numeric',
|
|
19321
|
+
month: 'short',
|
|
19322
|
+
year: 'numeric'
|
|
19323
|
+
});
|
|
19324
|
+
}
|
|
19325
|
+
catch {
|
|
19326
|
+
return dateStr;
|
|
19327
|
+
}
|
|
19328
|
+
}
|
|
19329
|
+
formatCabinLabel(cabin) {
|
|
19330
|
+
if (!cabin)
|
|
19331
|
+
return '';
|
|
19332
|
+
// Split CamelCase like 'PremiumEconomy' → 'Premium Economy'
|
|
19333
|
+
return cabin.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
19334
|
+
}
|
|
19335
|
+
/**
|
|
19336
|
+
* Render OBT's validationMessages array as a chatbot reply. Single message →
|
|
19337
|
+
* inline sentence; multiple → bullet list so each issue reads clearly.
|
|
19338
|
+
*/
|
|
19339
|
+
formatValidationMessages(messages) {
|
|
19340
|
+
if (messages.length === 1) {
|
|
19341
|
+
return `I couldn't run that search — ${messages[0]}. Please adjust and try again.`;
|
|
19342
|
+
}
|
|
19343
|
+
const bullets = messages.map((m) => `• ${m}`).join('\n');
|
|
19344
|
+
return `I couldn't run that search — please fix the following and try again:\n${bullets}`;
|
|
19345
|
+
}
|
|
19346
|
+
// ── Param population ─────────────────────────────────────────────────────
|
|
19347
|
+
/**
|
|
19348
|
+
* Tracks the last chatbot flight search's resolved airports so we can decide
|
|
19349
|
+
* whether a follow-up turn is a "refinement of the same journey" (e.g. swap
|
|
19350
|
+
* MAN → AMS for LPL → AMS — same metro, user is just nudging a parameter)
|
|
19351
|
+
* or a "fresh search" (e.g. MAN → AMS to BER → ATH — clearly unrelated, so
|
|
19352
|
+
* any sticky options like "direct only" should be cleared).
|
|
19353
|
+
*/
|
|
19354
|
+
lastSearch = null;
|
|
19355
|
+
static NEARBY_AIRPORT_KM = 150;
|
|
19356
|
+
static distanceKm(lat1, lng1, lat2, lng2) {
|
|
19357
|
+
const toRad = (d) => (d * Math.PI) / 180;
|
|
19358
|
+
const R = 6371;
|
|
19359
|
+
const dLat = toRad(lat2 - lat1);
|
|
19360
|
+
const dLng = toRad(lng2 - lng1);
|
|
19361
|
+
const a = Math.sin(dLat / 2) ** 2 +
|
|
19362
|
+
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;
|
|
19363
|
+
return 2 * R * Math.asin(Math.sqrt(a));
|
|
19364
|
+
}
|
|
19365
|
+
airportCoords(a) {
|
|
19366
|
+
const lat = a?.latitude ?? a?.lat ?? a?.airportDetail?.latitude;
|
|
19367
|
+
const lng = a?.longitude ?? a?.lng ?? a?.airportDetail?.longitude;
|
|
19368
|
+
if (typeof lat !== 'number' || typeof lng !== 'number')
|
|
19369
|
+
return null;
|
|
19370
|
+
return { lat, lng };
|
|
19371
|
+
}
|
|
19372
|
+
/**
|
|
19373
|
+
* Multi-city counterpart to `populateFlightParams`. Resolves each leg's
|
|
19374
|
+
* origin/destination airports, builds the `IMultiDestination[]` shape the
|
|
19375
|
+
* flight search expects, and switches `chosenSearchType` to multiCity.
|
|
19376
|
+
* Same-journey detection is skipped (the user is describing a fundamentally
|
|
19377
|
+
* new itinerary), so we always do a full reset.
|
|
19378
|
+
*/
|
|
19379
|
+
async populateMultiCityFlightParams(parameters) {
|
|
19380
|
+
const flightParams = this.searchService.searches[ServiceType.Flight];
|
|
19381
|
+
if (typeof flightParams.resetParams === 'function') {
|
|
19382
|
+
try {
|
|
19383
|
+
flightParams.resetParams();
|
|
19384
|
+
}
|
|
19385
|
+
catch (err) {
|
|
19386
|
+
console.warn('[ChatbotFlight] resetParams() failed (multi-city)', err);
|
|
19387
|
+
}
|
|
19388
|
+
}
|
|
19389
|
+
this.lastSearch = null;
|
|
19390
|
+
const legs = parameters.legs;
|
|
19391
|
+
const resolved = await Promise.all(legs.map(async (leg) => {
|
|
19392
|
+
const [orig, dest] = await Promise.all([
|
|
19393
|
+
this.resolveAirport(leg.departureCode, leg.departure),
|
|
19394
|
+
this.resolveAirport(leg.arrivalCode, leg.arrival)
|
|
19395
|
+
]);
|
|
19396
|
+
const timeCriteria = leg.timeCriteria === 'Arrive'
|
|
19397
|
+
? TimeWindow.Arrive
|
|
19398
|
+
: leg.timeCriteria === 'Depart'
|
|
19399
|
+
? TimeWindow.Depart
|
|
19400
|
+
: TimeWindow.Anytime;
|
|
19401
|
+
const legMoment = moment$1(leg.date);
|
|
19402
|
+
if (leg.time) {
|
|
19403
|
+
const [hh, mm] = this.normalizeTime(leg.time).match(/.{1,2}/g) || ['00', '00'];
|
|
19404
|
+
legMoment.hours(Number(hh)).minutes(Number(mm));
|
|
19405
|
+
}
|
|
19406
|
+
else {
|
|
19407
|
+
// Default to mid-morning so the time-window the backend builds
|
|
19408
|
+
// around the date is sensible when the user said "anytime".
|
|
19409
|
+
legMoment.hours(9).minutes(0);
|
|
19410
|
+
}
|
|
19411
|
+
return {
|
|
19412
|
+
orig,
|
|
19413
|
+
dest,
|
|
19414
|
+
date: legMoment,
|
|
19415
|
+
outTimeCriteria: timeCriteria,
|
|
19416
|
+
// Matches the multi-city form default (line 848 of
|
|
19417
|
+
// flight-enterprise-search.ts). 0 would mean a zero-minute window
|
|
19418
|
+
// and the backend returns no flights.
|
|
19419
|
+
outTimeFlexibility: 2
|
|
19420
|
+
};
|
|
19421
|
+
}));
|
|
19422
|
+
const missing = resolved.findIndex((r) => !r.orig || !r.dest);
|
|
19423
|
+
if (missing >= 0) {
|
|
19424
|
+
console.warn('[ChatbotFlight] Could not resolve airports for multi-city leg', missing + 1, legs[missing]);
|
|
19425
|
+
}
|
|
19426
|
+
// chosenSearchType must be set BEFORE multiDestination — the setter wipes
|
|
19427
|
+
// multiDestination when transitioning away from multiCity.
|
|
19428
|
+
flightParams.chosenSearchType = FlightSearchType.multiCity;
|
|
19429
|
+
flightParams.multiDestination = resolved;
|
|
19430
|
+
// Mirror the first leg to the standard depart/arrive fields so any code
|
|
19431
|
+
// (incl. the search form's ngOnInit fallback) that reads them still works.
|
|
19432
|
+
if (resolved[0]?.orig) {
|
|
19433
|
+
flightParams.departLocation = resolved[0].orig;
|
|
19434
|
+
}
|
|
19435
|
+
if (resolved[0]?.dest) {
|
|
19436
|
+
flightParams.arriveLocation = resolved[0].dest;
|
|
19437
|
+
}
|
|
19438
|
+
flightParams.outBoundDate = resolved[0]?.date;
|
|
19439
|
+
// departLocation/arriveLocation setters flip chosenSearchType implicitly
|
|
19440
|
+
// (via _isValid → form bindings), so re-assert it after mirroring.
|
|
19441
|
+
flightParams.chosenSearchType = FlightSearchType.multiCity;
|
|
19442
|
+
flightParams.multiDestination = resolved;
|
|
19443
|
+
// Shared optional fields (cabin, passengers, sort) still apply across all
|
|
19444
|
+
// legs — re-use the existing single/return handlers by piggy-backing on
|
|
19445
|
+
// them where they don't depend on departLocation/arrivalLocation.
|
|
19446
|
+
this.applySharedFlightParams(parameters, flightParams);
|
|
19447
|
+
}
|
|
19448
|
+
/**
|
|
19449
|
+
* Apply the subset of `populateFlightParams` that is leg-agnostic (cabin
|
|
19450
|
+
* class, passenger breakdown, sort, refundable flag). Shared between
|
|
19451
|
+
* single/return and multi-city paths.
|
|
19452
|
+
*/
|
|
19453
|
+
applySharedFlightParams(parameters, flightParams) {
|
|
19454
|
+
if (parameters.cabinClass) {
|
|
19455
|
+
const cc = String(parameters.cabinClass).trim().toLowerCase();
|
|
19456
|
+
if (cc && cc !== 'any' && cc !== 'all') {
|
|
19457
|
+
flightParams.cabinClass = this.mapCabinClass(parameters.cabinClass);
|
|
19458
|
+
}
|
|
19459
|
+
else if ('cabinClass' in flightParams && FlightCabinClass.Any) {
|
|
19460
|
+
flightParams.cabinClass = FlightCabinClass.Any;
|
|
19461
|
+
}
|
|
19462
|
+
}
|
|
19463
|
+
if (parameters.adults != null) {
|
|
19464
|
+
flightParams.adults = parameters.adults;
|
|
19465
|
+
}
|
|
19466
|
+
else if (parameters.passengers) {
|
|
19467
|
+
flightParams.adults = parameters.passengers;
|
|
19468
|
+
}
|
|
19469
|
+
if (parameters.children != null) {
|
|
19470
|
+
flightParams.children = parameters.children;
|
|
19471
|
+
}
|
|
19472
|
+
if (parameters.infants != null && 'infants' in flightParams) {
|
|
19473
|
+
flightParams.infants = parameters.infants;
|
|
19474
|
+
}
|
|
19475
|
+
}
|
|
19476
|
+
async populateFlightParams(parameters, departNgbDate) {
|
|
19477
|
+
const flightParams = this.searchService.searches[ServiceType.Flight];
|
|
19478
|
+
// Resolve the new turn's airports up-front so we can decide whether this
|
|
19479
|
+
// looks like the same journey (refinement) or a fresh search (full reset).
|
|
19480
|
+
const [from, to] = await Promise.all([
|
|
19481
|
+
this.resolveAirport(parameters.departureCode, parameters.departure),
|
|
19482
|
+
this.resolveAirport(parameters.arrivalCode, parameters.arrival)
|
|
19483
|
+
]);
|
|
19484
|
+
const fromCoords = this.airportCoords(from);
|
|
19485
|
+
const toCoords = this.airportCoords(to);
|
|
19486
|
+
// Decide whether to do a per-turn full reset of parameters.
|
|
19487
|
+
// - First chatbot search in this session → no previous state to inherit,
|
|
19488
|
+
// reset anyway to wipe any stale state from the form.
|
|
19489
|
+
// - Both origin AND destination within NEARBY_AIRPORT_KM of last search →
|
|
19490
|
+
// treat as a refinement of the same journey and DO NOT reset, so
|
|
19491
|
+
// constraints the user has already expressed ("direct", "BA", cabin)
|
|
19492
|
+
// persist even if the AI omits them in the follow-up tool call.
|
|
19493
|
+
// - Either origin OR destination moved further than the threshold →
|
|
19494
|
+
// treat as a new search and FULL reset.
|
|
19495
|
+
let sameJourney = false;
|
|
19496
|
+
if (this.lastSearch && fromCoords && toCoords) {
|
|
19497
|
+
const fromDist = ChatbotFlightSearchService.distanceKm(fromCoords.lat, fromCoords.lng, this.lastSearch.fromLat, this.lastSearch.fromLng);
|
|
19498
|
+
const toDist = ChatbotFlightSearchService.distanceKm(toCoords.lat, toCoords.lng, this.lastSearch.toLat, this.lastSearch.toLng);
|
|
19499
|
+
sameJourney =
|
|
19500
|
+
fromDist <= ChatbotFlightSearchService.NEARBY_AIRPORT_KM &&
|
|
19501
|
+
toDist <= ChatbotFlightSearchService.NEARBY_AIRPORT_KM;
|
|
19502
|
+
}
|
|
19503
|
+
if (!sameJourney) {
|
|
19504
|
+
// Per-turn full reset: wipe all previous parameters and then apply only
|
|
19505
|
+
// what's in `parameters`. Without this, the long-lived flight-search
|
|
19506
|
+
// singleton (shared with the OBT form) carries forward stale fields —
|
|
19507
|
+
// e.g. "AMS → MAN direct" then later "BER → ATH" would inherit "direct".
|
|
19508
|
+
// We call the search class's own resetParams() so any future fields
|
|
19509
|
+
// automatically get reset too.
|
|
19510
|
+
if (typeof flightParams.resetParams === 'function') {
|
|
19511
|
+
try {
|
|
19512
|
+
flightParams.resetParams();
|
|
19513
|
+
}
|
|
19514
|
+
catch (err) {
|
|
19515
|
+
console.warn('[ChatbotFlight] resetParams() failed', err);
|
|
19516
|
+
}
|
|
19517
|
+
}
|
|
19518
|
+
}
|
|
19519
|
+
if (fromCoords && toCoords) {
|
|
19520
|
+
this.lastSearch = {
|
|
19521
|
+
fromLat: fromCoords.lat,
|
|
19522
|
+
fromLng: fromCoords.lng,
|
|
19523
|
+
toLat: toCoords.lat,
|
|
19524
|
+
toLng: toCoords.lng
|
|
19525
|
+
};
|
|
19526
|
+
}
|
|
19527
|
+
// Targeted resets for fields the AI must be able to clear within the
|
|
19528
|
+
// same journey (e.g. "actually any airline" — the AI omits airlineCode
|
|
19529
|
+
// in the next call and we need to drop the previous KL). For a fresh
|
|
19530
|
+
// journey these are already cleared by resetParams above, so we only
|
|
19531
|
+
// re-touch them on same-journey turns.
|
|
19532
|
+
if (sameJourney) {
|
|
19533
|
+
if ('preferredCarriers' in flightParams) {
|
|
19534
|
+
try {
|
|
19535
|
+
flightParams.preferredCarriers = [null, null, null, null];
|
|
19536
|
+
}
|
|
19537
|
+
catch (err) {
|
|
19538
|
+
console.warn('[ChatbotFlight] failed to reset preferredCarriers', err);
|
|
19539
|
+
}
|
|
19540
|
+
}
|
|
19541
|
+
if ('cabinClass' in flightParams && (parameters.cabinClass || '').trim()) {
|
|
19542
|
+
// Only reset cabin if the AI is sending one — otherwise we want to
|
|
19543
|
+
// keep the previously-stated cabin for the same journey.
|
|
19544
|
+
try {
|
|
19545
|
+
flightParams.cabinClass = FlightCabinClass.Economy;
|
|
19546
|
+
}
|
|
19547
|
+
catch (err) {
|
|
19548
|
+
console.warn('[ChatbotFlight] failed to reset cabinClass', err);
|
|
19549
|
+
}
|
|
19550
|
+
}
|
|
19551
|
+
}
|
|
19552
|
+
flightParams.departDate = departNgbDate;
|
|
19553
|
+
if (parameters.departureTime) {
|
|
19554
|
+
flightParams.departTime = this.normalizeTime(parameters.departureTime);
|
|
19555
|
+
}
|
|
19556
|
+
if (parameters.returnDate) {
|
|
19557
|
+
const returnDate = moment$1(parameters.returnDate);
|
|
19558
|
+
flightParams.returnDate = {
|
|
19559
|
+
year: returnDate.year(),
|
|
19560
|
+
month: returnDate.month() + 1,
|
|
19561
|
+
day: returnDate.date()
|
|
19562
|
+
};
|
|
19563
|
+
flightParams.chosenSearchType = FlightSearchType.return;
|
|
19564
|
+
if (parameters.returnTime) {
|
|
19565
|
+
flightParams.arriveTime = this.normalizeTime(parameters.returnTime);
|
|
19566
|
+
}
|
|
19567
|
+
}
|
|
19568
|
+
else {
|
|
19569
|
+
flightParams.chosenSearchType = FlightSearchType.oneWay;
|
|
19570
|
+
}
|
|
19571
|
+
// Treat explicit "any"/empty values as "clear" rather than "lookup". The
|
|
19572
|
+
// AI sometimes sends `cabinClass: "any"` when the user removes a class
|
|
19573
|
+
// preference — without this guard `mapCabinClass` would fall back to
|
|
19574
|
+
// Economy regardless and a search where the user truly wants "any cabin"
|
|
19575
|
+
// would still be filtered to Economy.
|
|
19576
|
+
if (parameters.cabinClass) {
|
|
19577
|
+
const cc = String(parameters.cabinClass).trim().toLowerCase();
|
|
19578
|
+
if (cc && cc !== 'any' && cc !== 'all') {
|
|
19579
|
+
flightParams.cabinClass = this.mapCabinClass(parameters.cabinClass);
|
|
19580
|
+
}
|
|
19581
|
+
else if ('cabinClass' in flightParams && FlightCabinClass.Any) {
|
|
19582
|
+
flightParams.cabinClass = FlightCabinClass.Any;
|
|
19583
|
+
}
|
|
19584
|
+
}
|
|
19585
|
+
// Passenger breakdown — if the AI passed adults/children/infants, prefer
|
|
19586
|
+
// those; otherwise fall back to total `passengers` as adults.
|
|
19587
|
+
if (parameters.adults != null) {
|
|
19588
|
+
flightParams.adults = parameters.adults;
|
|
19589
|
+
}
|
|
19590
|
+
else if (parameters.passengers) {
|
|
19591
|
+
flightParams.adults = parameters.passengers;
|
|
19592
|
+
}
|
|
19593
|
+
if (parameters.children != null) {
|
|
19594
|
+
flightParams.children = parameters.children;
|
|
19595
|
+
}
|
|
19596
|
+
if (parameters.infants != null && 'infants' in flightParams) {
|
|
19597
|
+
flightParams.infants = parameters.infants;
|
|
19598
|
+
}
|
|
19599
|
+
// Stops / connections — 0 means direct-only. Also flip `directDefaultValue`
|
|
19600
|
+
// so that if the user later navigates to the flight search form (whose
|
|
19601
|
+
// ngOnInit re-derives maxConnections from this flag) the "Direct only"
|
|
19602
|
+
// checkbox stays ticked instead of being silently reset.
|
|
19603
|
+
if (parameters.maxConnections != null) {
|
|
19604
|
+
flightParams.maxConnections = parameters.maxConnections;
|
|
19605
|
+
if ('directDefaultValue' in flightParams) {
|
|
19606
|
+
flightParams.directDefaultValue = parameters.maxConnections === 0;
|
|
19607
|
+
}
|
|
19608
|
+
}
|
|
19609
|
+
if (parameters.includeNearbyAirports != null && 'includeNearbyAirports' in flightParams) {
|
|
19610
|
+
flightParams.includeNearbyAirports = parameters.includeNearbyAirports;
|
|
19611
|
+
}
|
|
19612
|
+
// Time criteria (Depart vs Arrive) + flexibility window. The form's
|
|
19613
|
+
// checkbox/radio binds to the TimeWindow enum ("Depart By" / "Arrive By"
|
|
19614
|
+
// / "Anytime"), so we must translate the short codes from the AI tool.
|
|
19615
|
+
const toTimeWindow = (val) => {
|
|
19616
|
+
const v = val.toLowerCase().replace(/[\s_-]+/g, '');
|
|
19617
|
+
if (v.startsWith('depart'))
|
|
19618
|
+
return TimeWindow.Depart;
|
|
19619
|
+
if (v.startsWith('arrive'))
|
|
19620
|
+
return TimeWindow.Arrive;
|
|
19621
|
+
if (v.startsWith('any'))
|
|
19622
|
+
return TimeWindow.Anytime;
|
|
19623
|
+
return null;
|
|
19624
|
+
};
|
|
19625
|
+
if (parameters.outboundTimeCriteria) {
|
|
19626
|
+
const mapped = toTimeWindow(parameters.outboundTimeCriteria);
|
|
19627
|
+
if (mapped)
|
|
19628
|
+
flightParams.outTimeCriteria = mapped;
|
|
19629
|
+
}
|
|
19630
|
+
if (parameters.returnTimeCriteria) {
|
|
19631
|
+
const mapped = toTimeWindow(parameters.returnTimeCriteria);
|
|
19632
|
+
if (mapped)
|
|
19633
|
+
flightParams.inTimeCriteria = mapped;
|
|
19634
|
+
}
|
|
19635
|
+
if (parameters.timeFlexibility != null) {
|
|
19636
|
+
flightParams.outTimeFlexibility = parameters.timeFlexibility;
|
|
19637
|
+
if (parameters.returnDate) {
|
|
19638
|
+
flightParams.inTimeFlexibility = parameters.timeFlexibility;
|
|
19639
|
+
}
|
|
19640
|
+
}
|
|
19641
|
+
// Via / transit airport.
|
|
19642
|
+
if (parameters.viaStation || parameters.viaCode) {
|
|
19643
|
+
const viaAirport = await this.resolveAirport(parameters.viaCode, parameters.viaStation || '');
|
|
19644
|
+
if (viaAirport) {
|
|
19645
|
+
flightParams.via = viaAirport;
|
|
19646
|
+
if ('showVia' in flightParams)
|
|
19647
|
+
flightParams.showVia = true;
|
|
19648
|
+
}
|
|
19649
|
+
}
|
|
19650
|
+
// Preferred airline → operator1 (which is preferredCarriers[0]).
|
|
19651
|
+
// The form's airline field is a typeahead backed by the searchAirlines
|
|
19652
|
+
// GraphQL query, so we MUST write a real {code, name} object resolved
|
|
19653
|
+
// from that source (an ad-hoc object with the wrong code wouldn't match
|
|
19654
|
+
// anything server-side). Use either the IATA code or the name as the
|
|
19655
|
+
// search term, prefer an exact IATA match, then an exact name match,
|
|
19656
|
+
// then a substring match.
|
|
19657
|
+
//
|
|
19658
|
+
// Skip entirely when the AI passes a "no preference" sentinel — null,
|
|
19659
|
+
// empty string, or the literal word "any"/"all". preferredCarriers was
|
|
19660
|
+
// already reset to all-nulls at the top of this method, so no carrier
|
|
19661
|
+
// filter will be applied to the search in that case.
|
|
19662
|
+
const rawAirlineTerm = (parameters.airlineCode || parameters.airline || '').toString().trim();
|
|
19663
|
+
const isAnyAirline = !rawAirlineTerm ||
|
|
19664
|
+
rawAirlineTerm.toLowerCase() === 'any' ||
|
|
19665
|
+
rawAirlineTerm.toLowerCase() === 'all';
|
|
19666
|
+
if (!isAnyAirline) {
|
|
19667
|
+
if (!Array.isArray(flightParams.preferredCarriers)) {
|
|
19668
|
+
try {
|
|
19669
|
+
flightParams.preferredCarriers = [];
|
|
19670
|
+
}
|
|
19671
|
+
catch (err) {
|
|
19672
|
+
console.warn('[ChatbotFlight] failed to seed preferredCarriers array', err);
|
|
19673
|
+
}
|
|
19674
|
+
}
|
|
19675
|
+
if (Array.isArray(flightParams.preferredCarriers)) {
|
|
19676
|
+
const term = (parameters.airlineCode || parameters.airline || '').trim();
|
|
19677
|
+
if (term.length >= 2) {
|
|
19678
|
+
try {
|
|
19679
|
+
const matches = await firstValueFrom(this.searchService.getAirlinesList(term));
|
|
19680
|
+
const list = matches || [];
|
|
19681
|
+
const upper = term.toUpperCase();
|
|
19682
|
+
const lower = term.toLowerCase();
|
|
19683
|
+
// Avoid silently selecting a regional/subsidiary carrier when the
|
|
19684
|
+
// user named the parent airline. E.g. "KLM" → should match
|
|
19685
|
+
// "KLM Royal Dutch Airlines" not "KLM Cityhopper" (which would
|
|
19686
|
+
// also match a naive substring/startsWith check first).
|
|
19687
|
+
const isSubsidiary = (name) => /\b(cityhopper|cityline|cityjet|express|regional|connect|shuttle|airlink|link|commuter)\b/i.test(name);
|
|
19688
|
+
const startsWith = list.filter((a) => (a.name || '').toLowerCase().startsWith(lower));
|
|
19689
|
+
const startsWithNonSub = startsWith.filter((a) => !isSubsidiary(a.name || ''));
|
|
19690
|
+
const containsNonSub = list.filter((a) => !isSubsidiary(a.name || '') && (a.name || '').toLowerCase().includes(lower));
|
|
19691
|
+
const resolved = list.find((a) => (a.code || '').toUpperCase() === upper) ||
|
|
19692
|
+
list.find((a) => (a.name || '').toLowerCase() === lower) ||
|
|
19693
|
+
startsWithNonSub[0] ||
|
|
19694
|
+
startsWith[0] ||
|
|
19695
|
+
containsNonSub[0] ||
|
|
19696
|
+
list.find((a) => (a.name || '').toLowerCase().includes(lower)) ||
|
|
19697
|
+
list[0];
|
|
19698
|
+
if (resolved) {
|
|
19699
|
+
flightParams.preferredCarriers[0] = {
|
|
19700
|
+
code: resolved.code,
|
|
19701
|
+
name: resolved.name
|
|
19702
|
+
};
|
|
19703
|
+
}
|
|
19704
|
+
}
|
|
19705
|
+
catch (err) {
|
|
19706
|
+
console.warn('[ChatbotFlightSearchService] airline lookup failed', err);
|
|
19707
|
+
}
|
|
19708
|
+
}
|
|
19709
|
+
}
|
|
19710
|
+
}
|
|
19711
|
+
// Apply the airports we already resolved at the top of this method.
|
|
19712
|
+
if (from) {
|
|
19713
|
+
flightParams.departLocation = from;
|
|
19714
|
+
}
|
|
19715
|
+
if (to) {
|
|
19716
|
+
flightParams.arriveLocation = to;
|
|
19717
|
+
}
|
|
19718
|
+
// Re-apply journey type after location resolution in case downstream
|
|
19719
|
+
// setters reset it (mirrors the rail handler safeguard).
|
|
19720
|
+
if (parameters.returnDate) {
|
|
19721
|
+
flightParams.chosenSearchType = FlightSearchType.return;
|
|
19722
|
+
}
|
|
19723
|
+
}
|
|
19724
|
+
mapCabinClass(value) {
|
|
19725
|
+
const normalised = value.replace(/\s+/g, '').toLowerCase();
|
|
19726
|
+
const match = Object.values(FlightCabinClass).find((c) => c.toLowerCase() === normalised);
|
|
19727
|
+
return match ?? FlightCabinClass.Economy;
|
|
19728
|
+
}
|
|
19729
|
+
normalizeTime(time) {
|
|
19730
|
+
const parsed = moment$1(time, ['H:mm', 'HH:mm', 'ha', 'h:mma', 'h a', 'h:mm a']);
|
|
19731
|
+
if (parsed.isValid()) {
|
|
19732
|
+
return parsed.format('HHmm');
|
|
19733
|
+
}
|
|
19734
|
+
return time.replace(':', '');
|
|
19735
|
+
}
|
|
19736
|
+
async resolveAirport(code, name) {
|
|
19737
|
+
if (code && code.length >= 3) {
|
|
19738
|
+
try {
|
|
19739
|
+
const byCode = await firstValueFrom(this.searchService.getAirportsList(code, null));
|
|
19740
|
+
if (byCode?.length > 0) {
|
|
19741
|
+
return byCode.find((a) => a.gateway === code) || byCode[0];
|
|
19742
|
+
}
|
|
19743
|
+
}
|
|
19744
|
+
catch {
|
|
19745
|
+
/* fall through to name search */
|
|
19746
|
+
}
|
|
19747
|
+
}
|
|
19748
|
+
if (name && name.length > 2) {
|
|
19749
|
+
try {
|
|
19750
|
+
const byName = await firstValueFrom(this.searchService.getAirportsList(name, null));
|
|
19751
|
+
if (byName?.length > 0) {
|
|
19752
|
+
return byName[0];
|
|
19753
|
+
}
|
|
19754
|
+
}
|
|
19755
|
+
catch {
|
|
19756
|
+
/* no results */
|
|
19757
|
+
}
|
|
19758
|
+
}
|
|
19759
|
+
return null;
|
|
19760
|
+
}
|
|
19761
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotFlightSearchService, deps: [{ token: ChatbotService }, { token: EnterpriseSearchService }, { token: ResultAwareService }, { token: UserService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
19762
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotFlightSearchService, providedIn: 'root' });
|
|
19763
|
+
}
|
|
19764
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotFlightSearchService, decorators: [{
|
|
19765
|
+
type: Injectable,
|
|
19766
|
+
args: [{ providedIn: 'root' }]
|
|
19767
|
+
}], ctorParameters: () => [{ type: ChatbotService }, { type: EnterpriseSearchService }, { type: ResultAwareService }, { type: UserService }] });
|
|
19768
|
+
|
|
19769
|
+
/** Maximum number of hotel cards to show in the chat widget. */
|
|
19770
|
+
const MAX_HOTEL_CARDS = 3;
|
|
19771
|
+
/** How many candidates to fetch availability for before slicing to MAX_HOTEL_CARDS. */
|
|
19772
|
+
const AVAILABILITY_CANDIDATE_COUNT = 6;
|
|
19773
|
+
/** Safety timeout (ms) for availability polling. */
|
|
19774
|
+
const HOTEL_AVAILABILITY_WAIT_MS = 30_000;
|
|
19775
|
+
/** How long to wait for JIT hotel rules (which set policyToken) before proceeding. */
|
|
19776
|
+
const JIT_RULES_WAIT_MS = 30_000;
|
|
19777
|
+
/**
|
|
19778
|
+
* Handles chatbot-driven hotel searches. Registered with
|
|
19779
|
+
* `ChatbotSearchDispatcherService`; not used directly.
|
|
19780
|
+
*
|
|
19781
|
+
* Populates the hotel search state from the chatbot's parameters, runs the
|
|
19782
|
+
* search headlessly, polls availability for the top candidates, then injects
|
|
19783
|
+
* a `ChatHotelResults` message so the chat widget can render hotel cards.
|
|
19784
|
+
* Works from any page — no search form component needs to be mounted.
|
|
19785
|
+
*/
|
|
19786
|
+
class ChatbotHotelSearchService {
|
|
19787
|
+
chatbotService;
|
|
19788
|
+
searchService;
|
|
19789
|
+
hotelAvalibilityService;
|
|
19790
|
+
resultAwareService;
|
|
19791
|
+
travelType = 'hotel';
|
|
19792
|
+
availabilitySubscription = null;
|
|
19793
|
+
constructor(chatbotService, searchService, hotelAvalibilityService, resultAwareService) {
|
|
19794
|
+
this.chatbotService = chatbotService;
|
|
19795
|
+
this.searchService = searchService;
|
|
19796
|
+
this.hotelAvalibilityService = hotelAvalibilityService;
|
|
19797
|
+
this.resultAwareService = resultAwareService;
|
|
19798
|
+
}
|
|
19799
|
+
async handle(parameters, autoSearch) {
|
|
19800
|
+
this.searchService.resetSearchChosenObjects();
|
|
19801
|
+
this.searchService.resetSearchPriorities();
|
|
19802
|
+
this.searchService.search_objects[ServiceType.Hotel].chosen = true;
|
|
19803
|
+
this.searchService.search_objects[ServiceType.Hotel].originalChosen = true;
|
|
19804
|
+
this.searchService.determineHighestSearchPriority();
|
|
19805
|
+
const departDate = moment$1(parameters.departureDate);
|
|
19806
|
+
const departNgbDate = {
|
|
19807
|
+
year: departDate.year(),
|
|
19808
|
+
month: departDate.month() + 1,
|
|
19809
|
+
day: departDate.date()
|
|
19810
|
+
};
|
|
19811
|
+
await this.populateHotelParams(parameters, departDate, departNgbDate);
|
|
19812
|
+
if (!autoSearch) {
|
|
19813
|
+
return;
|
|
19814
|
+
}
|
|
19815
|
+
await this.runSearch(parameters);
|
|
19816
|
+
}
|
|
19817
|
+
// ── Search + availability ─────────────────────────────────────────────────
|
|
19818
|
+
async runSearch(parameters) {
|
|
19819
|
+
const hotelSearch = this.searchService.searches[ServiceType.Hotel];
|
|
19820
|
+
this.resultAwareService.clearResults();
|
|
19821
|
+
let hasResults = false;
|
|
19822
|
+
this.chatbotService.setSearching(true, 'hotel');
|
|
19823
|
+
hasResults = await hotelSearch.startSearch();
|
|
19824
|
+
if (!hasResults) {
|
|
19825
|
+
this.injectNoResultsMessage();
|
|
19826
|
+
this.chatbotService.setSearching(false);
|
|
19827
|
+
return;
|
|
19828
|
+
}
|
|
19829
|
+
await firstValueFrom(hotelSearch.jitRulesReceived.pipe(filter((received) => received), take(1), timeout({ first: JIT_RULES_WAIT_MS, with: () => of(false) })));
|
|
19830
|
+
const candidates = hotelSearch.results.value.slice(0, AVAILABILITY_CANDIDATE_COUNT);
|
|
19831
|
+
const availabilitySubjects = candidates.map((hotel) => this.hotelAvalibilityService.getAvailabilityForId(hotelSearch.makeHotelAvalilityObject(hotel), hotel));
|
|
19832
|
+
const allRoomsReady$ = combineLatest(availabilitySubjects).pipe(filter((results) => results.every((r) => r?.rooms !== undefined)), take(1));
|
|
19833
|
+
const fetchingDone$ = this.hotelAvalibilityService.fetching.pipe(skipWhile((fetching) => !fetching), filter((fetching) => !fetching), take(1));
|
|
19834
|
+
this.availabilitySubscription?.unsubscribe();
|
|
19835
|
+
this.availabilitySubscription = race(allRoomsReady$, fetchingDone$, timer(HOTEL_AVAILABILITY_WAIT_MS))
|
|
19836
|
+
.pipe(take(1), catchError$1((err) => {
|
|
19837
|
+
console.warn('[ChatbotHotelSearchService] availability polling error', err);
|
|
19838
|
+
this.chatbotService.setSearching(false);
|
|
19839
|
+
return EMPTY;
|
|
19840
|
+
}))
|
|
19841
|
+
.subscribe(() => {
|
|
19842
|
+
candidates.forEach((hotel, i) => {
|
|
19843
|
+
const availability = availabilitySubjects[i].value;
|
|
19844
|
+
if (availability?.rooms !== undefined) {
|
|
19845
|
+
const existingRooms = hotel.availableRates?.rooms ?? [];
|
|
19846
|
+
const rooms = (availability.rooms ?? []).map((room) => {
|
|
19847
|
+
if (!room || room.policyToken)
|
|
19848
|
+
return room;
|
|
19849
|
+
const match = existingRooms.find((r) => r?.roomId === room.roomId);
|
|
19850
|
+
return match?.policyToken ? { ...room, policyToken: match.policyToken } : room;
|
|
19851
|
+
});
|
|
19852
|
+
hotel.availableRates = { ...availability, rooms };
|
|
19853
|
+
}
|
|
19854
|
+
});
|
|
19855
|
+
this.sortHotels(candidates, parameters.sortType);
|
|
19856
|
+
const hotelsWithPrices = candidates
|
|
19857
|
+
.filter((hotel) => (hotel.availableRates?.rooms ?? []).some((r) => r?.total > 0))
|
|
19858
|
+
.slice(0, MAX_HOTEL_CARDS);
|
|
19859
|
+
this.resultAwareService.captureHotelResults(hotelsWithPrices);
|
|
19860
|
+
const condensed = this.resultAwareService.getCondensedResults();
|
|
19861
|
+
if (!condensed.length) {
|
|
19862
|
+
this.injectNoResultsMessage();
|
|
19863
|
+
this.chatbotService.setSearching(false);
|
|
19864
|
+
return;
|
|
19865
|
+
}
|
|
19866
|
+
const cards = this.buildHotelCards(condensed, hotelsWithPrices);
|
|
19867
|
+
const hotelSearch = this.searchService.searches[ServiceType.Hotel];
|
|
19868
|
+
const checkin = hotelSearch?.checkin_date
|
|
19869
|
+
? moment$1(hotelSearch.checkin_date).format('D MMM')
|
|
19870
|
+
: '';
|
|
19871
|
+
const checkout = hotelSearch?.checkout_date
|
|
19872
|
+
? moment$1(hotelSearch.checkout_date).format('D MMM')
|
|
19873
|
+
: '';
|
|
19874
|
+
const dateRange = checkin && checkout ? `${checkin} - ${checkout}` : '';
|
|
19875
|
+
const searchHeader = [parameters.arrival, dateRange].filter(Boolean).join(' · ');
|
|
19876
|
+
const hotelResults = { cards, searchHeader, parameters };
|
|
19877
|
+
this.chatbotService.patchAssistantMessage({ hotelResults, state: 'RESULTS_READY' });
|
|
19878
|
+
this.chatbotService.setSearching(false);
|
|
19879
|
+
});
|
|
19880
|
+
}
|
|
19881
|
+
// ── Card building ─────────────────────────────────────────────────────────
|
|
19882
|
+
buildHotelCards(condensed, rawHotels) {
|
|
19883
|
+
return condensed.slice(0, MAX_HOTEL_CARDS).map((r, i) => {
|
|
19884
|
+
const raw = rawHotels[i];
|
|
19885
|
+
const rooms = (raw?.availableRates?.rooms ?? [])
|
|
19886
|
+
.filter((room) => room && !room.unavailable && room.total > 0)
|
|
19887
|
+
.map((room) => ({
|
|
19888
|
+
roomId: room.roomId,
|
|
19889
|
+
description: room.roomDescription || room.roomType || 'Standard Room',
|
|
19890
|
+
total: room.total,
|
|
19891
|
+
prpn: room.prpn,
|
|
19892
|
+
currencyCode: room.currencyCode ?? 'GBP',
|
|
19893
|
+
unavailable: !!room.unavailable,
|
|
19894
|
+
policyStatus: room.requiresReasonKeys?.length
|
|
19895
|
+
? 'ALLOW_WITH_REASON'
|
|
19896
|
+
: 'IN_POLICY'
|
|
19897
|
+
}));
|
|
19898
|
+
return {
|
|
19899
|
+
index: r.index,
|
|
19900
|
+
name: r.name ?? '',
|
|
19901
|
+
address: r.address ?? '',
|
|
19902
|
+
rating: r.rating ?? 0,
|
|
19903
|
+
distance: r.distance ?? 0,
|
|
19904
|
+
distanceUnit: r.distanceUnit ?? 'M',
|
|
19905
|
+
price: r.price,
|
|
19906
|
+
pricePerNight: r.pricePerNight ?? 0,
|
|
19907
|
+
dateRange: r.duration ?? '',
|
|
19908
|
+
policyStatus: r.policyStatus,
|
|
19909
|
+
policyMessages: r.policyMessages,
|
|
19910
|
+
co2: r.co2,
|
|
19911
|
+
currencyCode: rooms[0]?.currencyCode ?? 'GBP',
|
|
19912
|
+
rooms
|
|
19913
|
+
};
|
|
19914
|
+
});
|
|
19915
|
+
}
|
|
19916
|
+
injectNoResultsMessage() {
|
|
19917
|
+
this.chatbotService.injectMessage({
|
|
19918
|
+
id: `hotel-no-results-${Date.now()}`,
|
|
19919
|
+
role: 'assistant',
|
|
19920
|
+
text: 'No hotels found for your search. Try changing your dates or location.',
|
|
19921
|
+
timestamp: new Date(),
|
|
19922
|
+
state: 'SELECTING_RESULT'
|
|
19923
|
+
});
|
|
19924
|
+
}
|
|
19925
|
+
// ── Sort ──────────────────────────────────────────────────────────────────
|
|
19926
|
+
sortHotels(hotels, sortType) {
|
|
19927
|
+
switch (sortType) {
|
|
19928
|
+
case 'distance':
|
|
19929
|
+
hotels.sort((a, b) => (a.location?.distance ?? 0) - (b.location?.distance ?? 0));
|
|
19930
|
+
break;
|
|
19931
|
+
case 'name':
|
|
19932
|
+
hotels.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
19933
|
+
break;
|
|
19934
|
+
case 'preferred':
|
|
19935
|
+
hotels.sort((a, b) => {
|
|
19936
|
+
const aP = a.preferredType ? 1 : 0;
|
|
19937
|
+
const bP = b.preferredType ? 1 : 0;
|
|
19938
|
+
return bP - aP;
|
|
19939
|
+
});
|
|
19940
|
+
break;
|
|
19941
|
+
case 'cheapest':
|
|
19942
|
+
default:
|
|
19943
|
+
break;
|
|
19944
|
+
}
|
|
19945
|
+
}
|
|
19946
|
+
// ── Param population ──────────────────────────────────────────────────────
|
|
19947
|
+
async populateHotelParams(parameters, departDate, departNgbDate) {
|
|
19948
|
+
const hotelParams = this.searchService.searches[ServiceType.Hotel];
|
|
19949
|
+
hotelParams.checkin_date = departDate;
|
|
19950
|
+
hotelParams.checkin_date_ngb = departNgbDate;
|
|
19951
|
+
if (parameters.returnDate) {
|
|
19952
|
+
const checkoutDate = moment$1(parameters.returnDate);
|
|
19953
|
+
hotelParams.checkout_date = checkoutDate;
|
|
19954
|
+
hotelParams.checkout_date_ngb = {
|
|
19955
|
+
year: checkoutDate.year(),
|
|
19956
|
+
month: checkoutDate.month() + 1,
|
|
19957
|
+
day: checkoutDate.date()
|
|
19958
|
+
};
|
|
19959
|
+
}
|
|
19960
|
+
else {
|
|
19961
|
+
const checkoutDate = departDate.clone().add(1, 'day');
|
|
19962
|
+
hotelParams.checkout_date = checkoutDate;
|
|
19963
|
+
hotelParams.checkout_date_ngb = {
|
|
19964
|
+
year: checkoutDate.year(),
|
|
19965
|
+
month: checkoutDate.month() + 1,
|
|
19966
|
+
day: checkoutDate.date()
|
|
19967
|
+
};
|
|
19968
|
+
}
|
|
19969
|
+
hotelParams.no_of_rooms = parameters.rooms || 1;
|
|
19970
|
+
hotelParams.no_of_occupants = parameters.passengers || 1;
|
|
19971
|
+
if (parameters.hotelName) {
|
|
19972
|
+
hotelParams.hotel_name = parameters.hotelName;
|
|
19973
|
+
}
|
|
19974
|
+
if (parameters.hotelChain) {
|
|
19975
|
+
const term = parameters.hotelChain.toLowerCase();
|
|
19976
|
+
const prefix = parameters.hotelChain.slice(0, 3);
|
|
19977
|
+
const chains = await firstValueFrom(this.searchService.getHotelChain(prefix));
|
|
19978
|
+
if (chains?.length > 0) {
|
|
19979
|
+
const exact = chains.find((c) => c.name.toLowerCase() === term);
|
|
19980
|
+
const startsWithMatches = chains
|
|
19981
|
+
.filter((c) => c.name.toLowerCase().startsWith(term))
|
|
19982
|
+
.sort((a, b) => a.name.length - b.name.length);
|
|
19983
|
+
const includes = chains.find((c) => c.name.toLowerCase().includes(term));
|
|
19984
|
+
hotelParams.hotel_chain = exact ?? startsWithMatches[0] ?? includes ?? chains[0];
|
|
19985
|
+
}
|
|
19986
|
+
}
|
|
19987
|
+
if (parameters.distance) {
|
|
19988
|
+
hotelParams.distance = parameters.distance;
|
|
19989
|
+
}
|
|
19990
|
+
if (parameters.preferredOnly) {
|
|
19991
|
+
hotelParams.prefered_hotels_only = parameters.preferredOnly;
|
|
19992
|
+
}
|
|
19993
|
+
if (parameters.crownRatesOnly) {
|
|
19994
|
+
hotelParams.crownRatesOnly = parameters.crownRatesOnly;
|
|
19995
|
+
}
|
|
19996
|
+
if (parameters.propertyNumber) {
|
|
19997
|
+
hotelParams.location_type_select = LocationTypes.PropertyNumber;
|
|
19998
|
+
hotelParams.propertyNumber = parameters.propertyNumber;
|
|
19999
|
+
}
|
|
20000
|
+
else {
|
|
20001
|
+
const locationType = parameters.locationType || 'address';
|
|
20002
|
+
const locationTypeMap = {
|
|
20003
|
+
address: LocationTypes.Address,
|
|
20004
|
+
office: LocationTypes.Office,
|
|
20005
|
+
shortlist: LocationTypes.Shortlist,
|
|
20006
|
+
airport: LocationTypes.Address,
|
|
20007
|
+
trainstation: LocationTypes.Address
|
|
20008
|
+
};
|
|
20009
|
+
const resolvedLocationType = locationTypeMap[locationType] ?? LocationTypes.Address;
|
|
20010
|
+
hotelParams.location_type_select = resolvedLocationType;
|
|
20011
|
+
if (resolvedLocationType === LocationTypes.Office) {
|
|
20012
|
+
const offices = await firstValueFrom(this.searchService.getOfficeLocationsNoUserAddresses(''));
|
|
20013
|
+
if (offices?.length > 0) {
|
|
20014
|
+
const searchTerm = parameters.arrival?.toLowerCase() ?? '';
|
|
20015
|
+
const match = offices.find((o) => o.name.toLowerCase().includes(searchTerm)) ?? offices[0];
|
|
20016
|
+
hotelParams.office = match;
|
|
20017
|
+
}
|
|
20018
|
+
}
|
|
20019
|
+
else if (resolvedLocationType === LocationTypes.Address) {
|
|
20020
|
+
const arrivalRaw = parameters.arrival;
|
|
20021
|
+
const typeaheadQuery = arrivalRaw.replace(/,\s*/g, ' ').trim();
|
|
20022
|
+
const locations = await firstValueFrom(this.searchService.getDiscoverLocations(typeaheadQuery));
|
|
20023
|
+
if (locations?.length > 0) {
|
|
20024
|
+
const preferredType = locationType === 'trainstation'
|
|
20025
|
+
? LocationTypes.TrainStation
|
|
20026
|
+
: locationType === 'airport'
|
|
20027
|
+
? LocationTypes.Airport
|
|
20028
|
+
: LocationTypes.City;
|
|
20029
|
+
const normalised = typeaheadQuery
|
|
20030
|
+
.toLowerCase()
|
|
20031
|
+
.replace(/\b(station|airport|hub|central station)\b/g, '')
|
|
20032
|
+
.trim();
|
|
20033
|
+
const ofType = locations.filter((l) => l.type === preferredType);
|
|
20034
|
+
const match = ofType.find((l) => l.name.toLowerCase().includes(normalised.split(' ')[0])) ??
|
|
20035
|
+
ofType[0] ??
|
|
20036
|
+
locations.find((l) => l.type === LocationTypes.City) ??
|
|
20037
|
+
locations[0];
|
|
20038
|
+
hotelParams.location = match;
|
|
20039
|
+
}
|
|
20040
|
+
}
|
|
20041
|
+
else if (resolvedLocationType === LocationTypes.Shortlist && parameters.country) {
|
|
20042
|
+
const countryInput = parameters.country.toLowerCase();
|
|
20043
|
+
const match = this.searchService.countries?.find((c) => c.cCode.toLowerCase() === countryInput || c.cName.toLowerCase() === countryInput);
|
|
20044
|
+
if (match) {
|
|
20045
|
+
hotelParams.country = match;
|
|
20046
|
+
}
|
|
20047
|
+
}
|
|
20048
|
+
}
|
|
20049
|
+
}
|
|
20050
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotHotelSearchService, deps: [{ token: ChatbotService }, { token: EnterpriseSearchService }, { token: HotelAvalibilityService }, { token: ResultAwareService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
20051
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotHotelSearchService, providedIn: 'root' });
|
|
20052
|
+
}
|
|
20053
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotHotelSearchService, decorators: [{
|
|
20054
|
+
type: Injectable,
|
|
20055
|
+
args: [{ providedIn: 'root' }]
|
|
20056
|
+
}], ctorParameters: () => [{ type: ChatbotService }, { type: EnterpriseSearchService }, { type: HotelAvalibilityService }, { type: ResultAwareService }] });
|
|
20057
|
+
|
|
20058
|
+
/** Safety timeout (ms) waiting for eurostar results. */
|
|
20059
|
+
const EUROSTAR_RESULTS_WAIT_MS = 60_000;
|
|
20060
|
+
/** Maximum number of outbound/inbound cards to surface in the chat widget. */
|
|
20061
|
+
const MAX_EUROSTAR_CARDS = 3;
|
|
20062
|
+
/**
|
|
20063
|
+
* Handles chatbot-driven Eurostar searches. Registered with
|
|
20064
|
+
* `ChatbotSearchDispatcherService`; not used directly.
|
|
20065
|
+
*
|
|
20066
|
+
* Populates the Eurostar search state from the chatbot's parameters and, when
|
|
20067
|
+
* `autoSearch` is true, runs the search headlessly so it works from any page.
|
|
20068
|
+
* Once results arrive, builds `ChatEurostarResults` and patches them onto the
|
|
20069
|
+
* last assistant message via {@link ChatbotService.patchAssistantMessage}.
|
|
20070
|
+
*/
|
|
20071
|
+
class ChatbotEurostarSearchService {
|
|
20072
|
+
chatbotService;
|
|
20073
|
+
searchService;
|
|
20074
|
+
userService;
|
|
20075
|
+
travelType = 'eurostar';
|
|
20076
|
+
resultsSubscription = null;
|
|
20077
|
+
constructor(chatbotService, searchService, userService) {
|
|
20078
|
+
this.chatbotService = chatbotService;
|
|
20079
|
+
this.searchService = searchService;
|
|
20080
|
+
this.userService = userService;
|
|
20081
|
+
}
|
|
20082
|
+
async handle(parameters, autoSearch) {
|
|
20083
|
+
this.searchService.resetSearchChosenObjects();
|
|
20084
|
+
this.searchService.resetSearchPriorities();
|
|
20085
|
+
this.searchService.search_objects[ServiceType.Eurostar].chosen = true;
|
|
20086
|
+
this.searchService.search_objects[ServiceType.Eurostar].originalChosen = true;
|
|
20087
|
+
this.searchService.determineHighestSearchPriority();
|
|
20088
|
+
// Auto-add the signed-in user as the traveller when none is selected so
|
|
20089
|
+
// the search isn't blocked by the "add a traveller" validation rule.
|
|
20090
|
+
// Matches the rail handler pattern.
|
|
20091
|
+
if (this.searchService.travellerInformation.value.length === 0) {
|
|
20092
|
+
const selfTraveller = this.userService.createSelfTraveller();
|
|
20093
|
+
if (selfTraveller) {
|
|
20094
|
+
this.searchService.addTraveller(selfTraveller);
|
|
20095
|
+
}
|
|
20096
|
+
}
|
|
20097
|
+
const departDate = moment$1(parameters.departureDate);
|
|
20098
|
+
const departNgbDate = {
|
|
20099
|
+
year: departDate.year(),
|
|
20100
|
+
month: departDate.month() + 1,
|
|
20101
|
+
day: departDate.date()
|
|
20102
|
+
};
|
|
20103
|
+
await this.populateEurostarParams(parameters, departNgbDate);
|
|
20104
|
+
if (!autoSearch) {
|
|
20105
|
+
return;
|
|
20106
|
+
}
|
|
20107
|
+
await this.runSearch(parameters);
|
|
20108
|
+
}
|
|
20109
|
+
// ── Search ────────────────────────────────────────────────────────────────
|
|
20110
|
+
async runSearch(parameters) {
|
|
20111
|
+
const eurostarSearch = this.searchService.searches[ServiceType.Eurostar];
|
|
20112
|
+
// Surface OBT's existing validation (past dates, missing stations,
|
|
20113
|
+
// same origin/destination, return-before-depart, incompatible cabins)
|
|
20114
|
+
// before triggering the search — matches the rail handler pattern.
|
|
20115
|
+
const validationMessages = Array.isArray(eurostarSearch.validationMessages)
|
|
20116
|
+
? eurostarSearch.validationMessages.filter(Boolean)
|
|
20117
|
+
: [];
|
|
20118
|
+
if (validationMessages.length) {
|
|
20119
|
+
this.chatbotService.patchAssistantMessage({
|
|
20120
|
+
text: this.formatValidationMessages(validationMessages),
|
|
20121
|
+
state: 'RESULTS_READY'
|
|
20122
|
+
});
|
|
20123
|
+
this.chatbotService.setSearching(false);
|
|
20124
|
+
return;
|
|
20125
|
+
}
|
|
20126
|
+
this.subscribeToResults(parameters);
|
|
20127
|
+
let started = false;
|
|
20128
|
+
try {
|
|
20129
|
+
started = await eurostarSearch.startSearch();
|
|
20130
|
+
}
|
|
20131
|
+
catch (err) {
|
|
20132
|
+
console.warn('[ChatbotEurostarSearchService] startSearch threw', err);
|
|
20133
|
+
}
|
|
20134
|
+
if (!started) {
|
|
20135
|
+
this.resultsSubscription?.unsubscribe();
|
|
20136
|
+
this.resultsSubscription = null;
|
|
20137
|
+
// Re-read validation messages in case a late-firing setter pushed new
|
|
20138
|
+
// ones (e.g. station resolution returning nothing).
|
|
20139
|
+
const lateMessages = Array.isArray(eurostarSearch.validationMessages)
|
|
20140
|
+
? eurostarSearch.validationMessages.filter(Boolean)
|
|
20141
|
+
: [];
|
|
20142
|
+
this.chatbotService.patchAssistantMessage({
|
|
20143
|
+
text: lateMessages.length
|
|
20144
|
+
? this.formatValidationMessages(lateMessages)
|
|
20145
|
+
: `I couldn't run that Eurostar search — please check the dates and stations and try again.`,
|
|
20146
|
+
state: 'RESULTS_READY'
|
|
20147
|
+
});
|
|
20148
|
+
this.chatbotService.setSearching(false);
|
|
20149
|
+
}
|
|
20150
|
+
}
|
|
20151
|
+
/**
|
|
20152
|
+
* Render OBT's validationMessages array as a chatbot reply. Single message →
|
|
20153
|
+
* inline sentence; multiple → bullet list so each issue reads clearly.
|
|
20154
|
+
*/
|
|
20155
|
+
formatValidationMessages(messages) {
|
|
20156
|
+
if (messages.length === 1) {
|
|
20157
|
+
return `I couldn't run that Eurostar search — ${messages[0]}. Please adjust and try again.`;
|
|
20158
|
+
}
|
|
20159
|
+
const bullets = messages.map((m) => `• ${m}`).join('\n');
|
|
20160
|
+
return `I couldn't run that Eurostar search — please fix the following and try again:\n${bullets}`;
|
|
20161
|
+
}
|
|
20162
|
+
subscribeToResults(parameters) {
|
|
20163
|
+
this.resultsSubscription?.unsubscribe();
|
|
20164
|
+
const eurostarSearch = this.searchService.searches[ServiceType.Eurostar];
|
|
20165
|
+
this.chatbotService.setSearching(true, 'eurostar');
|
|
20166
|
+
// Use `isLoading` (true → false) as the search-complete signal, matching
|
|
20167
|
+
// the flight handler pattern. This correctly handles empty result sets
|
|
20168
|
+
// where waiting on results.length > 0 would hang until the timeout.
|
|
20169
|
+
const loadingSubject = eurostarSearch.isLoading;
|
|
20170
|
+
this.resultsSubscription = loadingSubject
|
|
20171
|
+
.pipe(skip(1), filter((loading) => loading === false), take(1), timeout$1(EUROSTAR_RESULTS_WAIT_MS), catchError$1((err) => {
|
|
20172
|
+
console.warn('[ChatbotEurostarSearchService] results timed out or errored', err);
|
|
20173
|
+
this.chatbotService.patchAssistantMessage({
|
|
20174
|
+
text: `The Eurostar search took too long to respond. Please try again in a moment.`,
|
|
20175
|
+
state: 'RESULTS_READY'
|
|
20176
|
+
});
|
|
20177
|
+
this.chatbotService.setSearching(false);
|
|
20178
|
+
return EMPTY;
|
|
20179
|
+
}))
|
|
20180
|
+
.subscribe(() => {
|
|
20181
|
+
const rawResults = eurostarSearch.results?.value ?? [];
|
|
20182
|
+
const isReturn = !!parameters.returnDate;
|
|
20183
|
+
// Results may come as EurostarResults[] (with .outbound/.inbound) or
|
|
20184
|
+
// as a flat array of items depending on the supplier path.
|
|
20185
|
+
const outboundItems = this.extractItems(rawResults, 'outbound');
|
|
20186
|
+
const inboundItems = isReturn ? this.extractItems(rawResults, 'inbound') : [];
|
|
20187
|
+
const outbound = outboundItems
|
|
20188
|
+
.slice(0, MAX_EUROSTAR_CARDS)
|
|
20189
|
+
.map((item, i) => this.buildCard(item, i));
|
|
20190
|
+
const inbound = inboundItems
|
|
20191
|
+
.slice(0, MAX_EUROSTAR_CARDS)
|
|
20192
|
+
.map((item, i) => this.buildCard(item, i));
|
|
20193
|
+
const eurostarResults = {
|
|
20194
|
+
outbound,
|
|
20195
|
+
...(isReturn && inbound.length ? { inbound } : {}),
|
|
20196
|
+
isReturn,
|
|
20197
|
+
parameters
|
|
20198
|
+
};
|
|
20199
|
+
this.chatbotService.patchAssistantMessage({ eurostarResults, state: 'RESULTS_READY' });
|
|
20200
|
+
this.chatbotService.setSearching(false);
|
|
20201
|
+
});
|
|
20202
|
+
}
|
|
20203
|
+
// ── Result helpers ────────────────────────────────────────────────────────
|
|
20204
|
+
extractItems(results, direction) {
|
|
20205
|
+
if (!results?.length) {
|
|
20206
|
+
return [];
|
|
20207
|
+
}
|
|
20208
|
+
// EurostarResults shape: [{ outbound: EurostarItem[], inbound: EurostarItem[] }].
|
|
20209
|
+
// Detect by scanning any item rather than the first, in case the first
|
|
20210
|
+
// wrapper is empty for one direction.
|
|
20211
|
+
const hasWrappedShape = results.some((r) => r && direction in r);
|
|
20212
|
+
if (hasWrappedShape) {
|
|
20213
|
+
return results.flatMap((r) => r?.[direction] ?? []);
|
|
20214
|
+
}
|
|
20215
|
+
// Flat array of EurostarQuoteResult journeys
|
|
20216
|
+
return results;
|
|
20217
|
+
}
|
|
20218
|
+
buildCard(item, index) {
|
|
20219
|
+
const fares = [];
|
|
20220
|
+
for (const cls of ['standardFare', 'standardPremierFare', 'businessPremierFare']) {
|
|
20221
|
+
const fare = item[cls];
|
|
20222
|
+
if (!fare || fare.isDisabled) {
|
|
20223
|
+
continue;
|
|
20224
|
+
}
|
|
20225
|
+
const isUnavailable = !!fare.unavailable;
|
|
20226
|
+
fares.push({
|
|
20227
|
+
class: fare.class ?? cls,
|
|
20228
|
+
price: fare.price ?? 0,
|
|
20229
|
+
currency: fare.currency ?? 'GBP',
|
|
20230
|
+
policyStatus: isUnavailable
|
|
20231
|
+
? 'UNAVAILABLE'
|
|
20232
|
+
: fare.requiresReasonKeys?.length
|
|
20233
|
+
? 'ALLOW_WITH_REASON'
|
|
20234
|
+
: 'IN_POLICY',
|
|
20235
|
+
policyMessages: fare.requiresReasonMessages ?? [],
|
|
20236
|
+
unavailable: isUnavailable
|
|
20237
|
+
});
|
|
20238
|
+
}
|
|
20239
|
+
return {
|
|
20240
|
+
index,
|
|
20241
|
+
timeDeparture: item.timeDeparture ?? '',
|
|
20242
|
+
timeArrival: item.timeArrival ?? '',
|
|
20243
|
+
duration: item.duration ?? '',
|
|
20244
|
+
fares,
|
|
20245
|
+
co2PerPassenger: item.co2PerPassenger ?? 0
|
|
20246
|
+
};
|
|
20247
|
+
}
|
|
20248
|
+
// ── Param population ──────────────────────────────────────────────────────
|
|
20249
|
+
async populateEurostarParams(parameters, departNgbDate) {
|
|
20250
|
+
const eurostarParams = this.searchService.searches[ServiceType.Eurostar];
|
|
20251
|
+
eurostarParams.departDate = departNgbDate;
|
|
20252
|
+
if (parameters.departureTime) {
|
|
20253
|
+
eurostarParams.departTime = this.normalizeTime(parameters.departureTime);
|
|
20254
|
+
eurostarParams.outTimeCriteria =
|
|
20255
|
+
parameters.outboundTimeCriteria === 'Arrive' ? TimeWindow.Arrive : TimeWindow.Depart;
|
|
20256
|
+
}
|
|
20257
|
+
else {
|
|
20258
|
+
eurostarParams.outTimeCriteria = TimeWindow.Anytime;
|
|
20259
|
+
}
|
|
20260
|
+
if (parameters.returnDate) {
|
|
20261
|
+
const returnDate = moment$1(parameters.returnDate);
|
|
20262
|
+
eurostarParams.returnDate = {
|
|
20263
|
+
year: returnDate.year(),
|
|
20264
|
+
month: returnDate.month() + 1,
|
|
20265
|
+
day: returnDate.date()
|
|
20266
|
+
};
|
|
20267
|
+
eurostarParams.chosenSearchType = EurostarSearchType.return;
|
|
20268
|
+
if (parameters.returnTime) {
|
|
20269
|
+
eurostarParams.arriveTime = this.normalizeTime(parameters.returnTime);
|
|
20270
|
+
eurostarParams.inTimeCriteria =
|
|
20271
|
+
parameters.returnTimeCriteria === 'Arrive' ? TimeWindow.Arrive : TimeWindow.Depart;
|
|
20272
|
+
}
|
|
20273
|
+
else {
|
|
20274
|
+
eurostarParams.inTimeCriteria = TimeWindow.Anytime;
|
|
20275
|
+
}
|
|
20276
|
+
}
|
|
20277
|
+
else {
|
|
20278
|
+
eurostarParams.chosenSearchType = EurostarSearchType.oneWay;
|
|
20279
|
+
}
|
|
20280
|
+
// Map the ai-agents cabin class string to the FlightCabinClass enum values
|
|
20281
|
+
// used by the Eurostar search class internals. Always set both outbound
|
|
20282
|
+
// and return cabin classes — `updateSerchQuery` calls
|
|
20283
|
+
// `turnCabinTypeToName(returnCabinClass)` on return trips and crashes if
|
|
20284
|
+
// it's undefined. Keeping them in sync also satisfies the cabin-match
|
|
20285
|
+
// validation rule (`checkCabinMatchisValid`).
|
|
20286
|
+
const mappedCabin = this.mapCabinClass(parameters.cabinClass);
|
|
20287
|
+
eurostarParams.cabinClass = mappedCabin;
|
|
20288
|
+
eurostarParams.returnCabinClass = mappedCabin;
|
|
20289
|
+
// Always set maxConnections so a previous "direct only" search doesn't
|
|
20290
|
+
// persist when the user removes that constraint. `null` = no limit.
|
|
20291
|
+
eurostarParams.maxConnections =
|
|
20292
|
+
parameters.maxConnections !== undefined && parameters.maxConnections !== null
|
|
20293
|
+
? parameters.maxConnections
|
|
20294
|
+
: null;
|
|
20295
|
+
const [fromStation, toStation] = await Promise.all([
|
|
20296
|
+
this.resolveStation(parameters.departureCode, parameters.departure),
|
|
20297
|
+
this.resolveStation(parameters.arrivalCode, parameters.arrival)
|
|
20298
|
+
]);
|
|
20299
|
+
if (fromStation) {
|
|
20300
|
+
eurostarParams.departLocation = fromStation;
|
|
20301
|
+
}
|
|
20302
|
+
if (toStation) {
|
|
20303
|
+
eurostarParams.arriveLocation = toStation;
|
|
20304
|
+
}
|
|
20305
|
+
}
|
|
20306
|
+
mapCabinClass(cabinClass) {
|
|
20307
|
+
switch (cabinClass) {
|
|
20308
|
+
case 'Standard':
|
|
20309
|
+
return FlightCabinClass.Economy;
|
|
20310
|
+
case 'StandardPremier':
|
|
20311
|
+
return FlightCabinClass.PremiumEconomy;
|
|
20312
|
+
case 'BusinessPremier':
|
|
20313
|
+
return FlightCabinClass.Business;
|
|
20314
|
+
default:
|
|
20315
|
+
return FlightCabinClass.Any;
|
|
20316
|
+
}
|
|
20317
|
+
}
|
|
20318
|
+
async resolveStation(code, name) {
|
|
20319
|
+
if (code) {
|
|
20320
|
+
try {
|
|
20321
|
+
const byCode = await firstValueFrom(this.searchService.lookupEurostarStations(code));
|
|
20322
|
+
if (byCode?.length > 0) {
|
|
20323
|
+
return byCode.find((s) => s.gateway === code) ?? byCode[0];
|
|
20324
|
+
}
|
|
20325
|
+
}
|
|
20326
|
+
catch (err) {
|
|
20327
|
+
console.warn('[ChatbotEurostarSearchService] station lookup by code failed', { code }, err);
|
|
20328
|
+
}
|
|
20329
|
+
}
|
|
20330
|
+
if (name) {
|
|
20331
|
+
try {
|
|
20332
|
+
const byName = await firstValueFrom(this.searchService.lookupEurostarStations(name));
|
|
20333
|
+
if (byName?.length > 0) {
|
|
20334
|
+
return byName[0];
|
|
20335
|
+
}
|
|
20336
|
+
}
|
|
20337
|
+
catch (err) {
|
|
20338
|
+
console.warn('[ChatbotEurostarSearchService] station lookup by name failed', { name }, err);
|
|
20339
|
+
}
|
|
20340
|
+
}
|
|
20341
|
+
return null;
|
|
20342
|
+
}
|
|
20343
|
+
normalizeTime(time) {
|
|
20344
|
+
const parsed = moment$1(time, ['HH:mm', 'H:mm', 'ha', 'h:mma', 'HHmm']);
|
|
20345
|
+
return parsed.isValid() ? parsed.format('HHmm') : time;
|
|
20346
|
+
}
|
|
20347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotEurostarSearchService, deps: [{ token: ChatbotService }, { token: EnterpriseSearchService }, { token: UserService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
20348
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotEurostarSearchService, providedIn: 'root' });
|
|
20349
|
+
}
|
|
20350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotEurostarSearchService, decorators: [{
|
|
20351
|
+
type: Injectable,
|
|
20352
|
+
args: [{ providedIn: 'root' }]
|
|
20353
|
+
}], ctorParameters: () => [{ type: ChatbotService }, { type: EnterpriseSearchService }, { type: UserService }] });
|
|
20354
|
+
|
|
20355
|
+
/**
|
|
20356
|
+
* Routes `ChatbotService.fillSearchForm$` events to per-travel-type handlers.
|
|
20357
|
+
*
|
|
20358
|
+
* **To add a new travel type (hotel / eurostar / taxi / …):**
|
|
20359
|
+
* 1. Create a new service implementing `ChatbotTravelHandler` (use
|
|
20360
|
+
* `ChatbotRailSearchService` or `ChatbotFlightSearchService` as a
|
|
20361
|
+
* reference implementation).
|
|
20362
|
+
* 2. Inject it into this dispatcher and add it to {@link handlers}.
|
|
20363
|
+
* 3. Nothing else needs to change — the consuming app calls {@link init}
|
|
20364
|
+
* once (typically from its root component's `ngOnInit`), and search
|
|
20365
|
+
* forms can ask {@link handles} which travel types to skip.
|
|
20366
|
+
*/
|
|
20367
|
+
class ChatbotSearchDispatcherService {
|
|
20368
|
+
chatbotService;
|
|
20369
|
+
initialised = false;
|
|
20370
|
+
handlers;
|
|
20371
|
+
constructor(chatbotService, rail, flight, hotel, eurostar) {
|
|
20372
|
+
this.chatbotService = chatbotService;
|
|
20373
|
+
this.handlers = [rail, flight, hotel, eurostar];
|
|
20374
|
+
}
|
|
20375
|
+
/** Call once at app start. Idempotent. */
|
|
20376
|
+
init() {
|
|
20377
|
+
if (this.initialised) {
|
|
20378
|
+
return;
|
|
20379
|
+
}
|
|
20380
|
+
this.initialised = true;
|
|
20381
|
+
this.chatbotService.fillSearchForm$.subscribe(({ parameters, autoSearch }) => {
|
|
20382
|
+
const handler = this.findHandler(parameters?.travelType);
|
|
20383
|
+
if (!handler) {
|
|
20384
|
+
// No global handler registered for this travel type — falls through
|
|
20385
|
+
// to the consuming app's per-form flow.
|
|
20386
|
+
return;
|
|
20387
|
+
}
|
|
20388
|
+
handler.handle(parameters, autoSearch).catch((err) => {
|
|
20389
|
+
console.error(`[ChatbotSearchDispatcherService] handler '${parameters.travelType}' failed:`, err);
|
|
20390
|
+
});
|
|
20391
|
+
});
|
|
20392
|
+
}
|
|
20393
|
+
/**
|
|
20394
|
+
* Whether a global handler is registered for the given travel type. Used by
|
|
20395
|
+
* search forms to know which travel types they should skip (avoiding
|
|
20396
|
+
* double-handling).
|
|
20397
|
+
*/
|
|
20398
|
+
handles(travelType) {
|
|
20399
|
+
return !!this.findHandler(travelType);
|
|
20400
|
+
}
|
|
20401
|
+
findHandler(travelType) {
|
|
20402
|
+
if (!travelType) {
|
|
20403
|
+
return undefined;
|
|
20404
|
+
}
|
|
20405
|
+
return this.handlers.find((h) => h.travelType === travelType);
|
|
20406
|
+
}
|
|
20407
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotSearchDispatcherService, deps: [{ token: ChatbotService }, { token: ChatbotRailSearchService }, { token: ChatbotFlightSearchService }, { token: ChatbotHotelSearchService }, { token: ChatbotEurostarSearchService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
20408
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotSearchDispatcherService, providedIn: 'root' });
|
|
20409
|
+
}
|
|
20410
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: ChatbotSearchDispatcherService, decorators: [{
|
|
20411
|
+
type: Injectable,
|
|
20412
|
+
args: [{ providedIn: 'root' }]
|
|
20413
|
+
}], ctorParameters: () => [{ type: ChatbotService }, { type: ChatbotRailSearchService }, { type: ChatbotFlightSearchService }, { type: ChatbotHotelSearchService }, { type: ChatbotEurostarSearchService }] });
|
|
20414
|
+
|
|
17828
20415
|
class BasketPanelComponent extends WithSubscriptionComponent {
|
|
17829
20416
|
basketService;
|
|
17830
20417
|
userService;
|
|
@@ -17934,13 +20521,17 @@ class BasketPanelComponent extends WithSubscriptionComponent {
|
|
|
17934
20521
|
// /itinerary
|
|
17935
20522
|
const requiresApproval = this.selectedBasket.subject.value.requiresManualApproval;
|
|
17936
20523
|
const navigateTo = this.basketService.continueSearchAndBook(basketItems, this.getItineraryUrl(this.selectedBasket.id), this.searchService);
|
|
17937
|
-
if (navigateTo &&
|
|
20524
|
+
if (navigateTo &&
|
|
20525
|
+
!(navigateTo[0] === '/itinerary' || navigateTo[0] === '/basket/booking-details')) {
|
|
20526
|
+
// ! - itinerary - lightning - booking-details -scion
|
|
17938
20527
|
this.router.navigate(navigateTo);
|
|
17939
20528
|
}
|
|
17940
20529
|
else if (navigateTo) {
|
|
17941
20530
|
if (requiresApproval) {
|
|
17942
20531
|
console.warn(`+++ Basket requires approval - display warning modal +++`);
|
|
17943
|
-
this.modalService
|
|
20532
|
+
this.modalService
|
|
20533
|
+
.open(ModalTypes.ApprovalWarningModalComponent, { backdrop: 'static' })
|
|
20534
|
+
.then((result) => {
|
|
17944
20535
|
if (result) {
|
|
17945
20536
|
console.log(`+++ User has selected continue +++`);
|
|
17946
20537
|
this.router.navigate(navigateTo);
|
|
@@ -18085,7 +20676,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
18085
20676
|
type: Pipe,
|
|
18086
20677
|
args: [{
|
|
18087
20678
|
name: 'memoize',
|
|
18088
|
-
pure: true
|
|
20679
|
+
pure: true //can be omitted as default value
|
|
18089
20680
|
}]
|
|
18090
20681
|
}] });
|
|
18091
20682
|
|
|
@@ -18109,5 +20700,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImpo
|
|
|
18109
20700
|
* Generated bundle index. Do not edit.
|
|
18110
20701
|
*/
|
|
18111
20702
|
|
|
18112
|
-
export { AcceptNewPriceDocument, AcceptNewPriceGQL, AcceptNewPriceUpdater, AccessibilityInfoFragmentDoc, AddBasketItemCustomRemarkDocument, AddBasketItemCustomRemarkGQL, AddBasketItemCustomRemarkUpdater, AddBasketItemMarkupDocument, AddBasketItemMarkupGQL, AddBasketItemMarkupUpdater, AddFavouriteUserDocument, AddFavouriteUserGQL, AddFavouriteUserUpdater, AddGuestToBasketItemUpdater, AddGuestsToBasketItemDocument, AddGuestsToBasketItemGQL, AddItemToBasketDocument, AddItemToBasketGQL, AddItemToBasketUpdater, AddRedirectApproverDocument, AddRedirectApproverGQL, AddRedirectApproverUpdater, AddToBasketFieldsFragmentDoc, AddUpdatedItemToBasketDocument, AddUpdatedItemToBasketGQL, AddUpdatedItemToBasketUpdater, AddUserToBasketItemUpdater, AddUsersToBasketItemDocument, AddUsersToBasketItemGQL, AddressType, AmendBookingDocument, AmendBookingGQL, AmendBookingUpdater, ApexxCardStatus, ApexxCreateCardDocument, ApexxCreateCardGQL, ApexxDeleteCardDocument, ApexxDeleteCardGQL, ApexxDeleteCardUpdater, ApexxListCardsDocument, ApexxListCardsGQL, ApexxUpdateCardDocument, ApexxUpdateCardGQL, ApexxUpdateCardUpdater, ApplyJitFlightRulesDocument, ApplyJitFlightRulesGQL, ApplyJitHotelRulesDocument, ApplyJitHotelRulesGQL, ApprovalEventType, ApproverUserFieldsFragmentDoc, AuthHttpService, BaggageType, BannerDisplayTarget, BasketFieldsFragmentDoc, BasketItemStatus, BasketMinimalFieldsFragmentDoc, BasketPanelComponent, BasketStatus, BeforeAmendCabSearchDocument, BeforeAmendCabSearchFetcher, BeforeAmendCabSearchGQL, BeforeAmendCarHireSearchDocument, BeforeAmendCarHireSearchFetcher, BeforeAmendCarHireSearchGQL, BookBasketDocument, BookBasketFieldsFragmentDoc, BookBasketGQL, BookBasketUpdater, BookerType, CabHireVehicleType, CabhireQuoteFetcher, CanAmendBookingDocument, CanAmendBookingFetcher, CanAmendBookingGQL, CancelBookingDocument, CancelBookingGQL, CancelBookingUpdater, CarHireAvailabilityDetailFetcher, CarHireAvailabilityDetailRequestDocument, CarHireAvailabilityDetailRequestGQL, CarHireClass, CarHireTransmission, CarHireType, CarbonPolicyDuration, CarbonPolicySource, CarhireQuoteFetcher, ChangeBasketOwnershipDocument, ChangeBasketOwnershipGQL, ChangeBasketOwnershipUpdater, ChangeMode, ChatbotService, CheckForDuplicateBookingsDocument, CheckForDuplicateBookingsFetcher, CheckForDuplicateBookingsGQL, CheckIfBasketRequiresApprovalDocument, CheckIfBasketRequiresApprovalFetcher, CheckIfBasketRequiresApprovalGQL, CompanyType, ConfirmMessagesDocument, ConfirmMessagesGQL, ConvertCurrencyDocument, ConvertCurrencyFetcher, ConvertCurrencyGQL, CreateBasketDocument, CreateBasketFieldsFragmentDoc, CreateBasketFromDraftDocument, CreateBasketFromDraftGQL, CreateBasketFromDraftUpdater, CreateBasketGQL, CreateBasketNoteDocument, CreateBasketNoteGQL, CreateBasketNoteUpdater, CreateBasketUpdater, CreateGuestDocument, CreateGuestGQL, CreateGuestUpdater, CreateItineraryExchangeDocument, CreateItineraryExchangeGQL, CurrencyCode, CycleInfoFragmentDoc, DeleteAllRecentSearchesDocument, DeleteAllRecentSearchesGQL, DeleteAllRecentSearchesUpdater, DeleteBasketDocument, DeleteBasketGQL, DeleteBasketUpdater, DeleteFavouriteSearchDocument, DeleteFavouriteSearchGQL, DeleteFavouriteSearchUpdater, DeleteGuestDocument, DeleteGuestGQL, DeleteGuestUpdater, DeleteRecentSearchDocument, DeleteRecentSearchGQL, DeleteUserAddressDocument, DeleteUserAddressGQL, DiscountType, DistanceTypes, DivisionFieldsFragmentDoc, DocGender, DocType, DraftBasketType, EditUserAddressDocument, EditUserAddressGQL, EditUserAddressUpdater, EditUserDocument, EditUserGQL, EmailAmbulanceBookingFetcher, EmailAmbulanceBookingReqDocument, EmailAmbulanceBookingReqGQL, EmailApartmentBookingFetcher, EmailApartmentBookingReqDocument, EmailApartmentBookingReqGQL, EmailBasketDocument, EmailBasketFetcher, EmailBasketGQL, EmailFerryBookingFetcher, EmailFerryBookingReqDocument, EmailFerryBookingReqGQL, EmailMeetingRoomBookingFetcher, EmailMeetingRoomBookingReqDocument, EmailMeetingRoomBookingReqGQL, EmailSeasonTicketBookingFetcher, EmailSeasonTicketBookingReqDocument, EmailSeasonTicketBookingReqGQL, EntLocationsByCityDocument, EntLocationsByCityGQL, EntLocationsByPostcodeDocument, EntLocationsByPostcodeGQL, EnterpriseBasketService, EnterpriseMyBookingsService, EnterpriseSearchService, Environment, ErrorsFragmentDoc, EurostarQuoteFetcher, EventMessenager, EvolviSeatmapsAreEnabledDocument, EvolviSeatmapsAreEnabledGQL, ExchangeType, ExternalHttpService, FailedEmail, FareRuleType, FareType, FerryOrEurotunnel, FlightCabinClass, FlightItineraryFieldsFragmentDoc, FlightPassengerType, FlightQuoteFetcher, GenerateBasketPdfDocument, GenerateBasketPdfFetcher, GenerateBasketPdfGQL, GetAirAvailabilityDocument, GetAirAvailabilityFetcher, GetAirAvailabilityGQL, GetAllAirlinesDocument, GetAllAirlinesFetcher, GetAllAirlinesGQL, GetApexxListCardsFetcher, GetBannerDocument, GetBannerFetcher, GetBannerGQL, GetBasketApprovalInfoDocument, GetBasketApprovalInfoFetcher, GetBasketApprovalInfoGQL, GetBasketApprovalTimestampsDocument, GetBasketApprovalTimestampsGQL, GetBasketCo2InfoFetcher, GetBasketFetcher, GetBasketItemCustomRemarksDocument, GetBasketItemCustomRemarksFetcher, GetBasketItemCustomRemarksGQL, GetBasketNotesDocument, GetBasketNotesFetcher, GetBasketNotesGQL, GetBasketQuoteDocument, GetBasketQuoteFetcher, GetBasketQuoteGQL, GetBrandedFaresDocument, GetBrandedFaresGQL, GetCancellationInfoDocument, GetCancellationInfoFetcher, GetCancellationInfoGQL, GetCarHireDepotsDocument, GetCarHireDepotsGQL, GetCarHireProvidersDocument, GetCarHireProvidersFetcher, GetCarHireProvidersGQL, GetCompaniesDocument, GetCompaniesFetcher, GetCompaniesGQL, GetCompanyDocument, GetCompanyFetcher, GetCompanyGQL, GetConfermaQuicklistDocument, GetConfermaQuicklistGQL, GetConfermaRoomImagesDocument, GetConfermaRoomImagesGQL, GetCovidMicrositeTokenDocument, GetCovidMicrositeTokenFetcher, GetCovidMicrositeTokenGQL, GetCurrencyConversionRatesDocument, GetCurrencyConversionRatesFetcher, GetCurrencyConversionRatesGQL, GetDivisionDocument, GetDivisionGQL, GetDraftBasketsDocument, GetDraftBasketsFetcher, GetDraftBasketsGQL, GetEmptyUserMiDefaultValuesDocument, GetEmptyUserMiDefaultValuesFetcher, GetEmptyUserMiDefaultValuesGQL, GetEntLocationByPostcode, GetEvolviSeatmapsAreEnabledFetcher, GetFerryPortsDocument, GetFerryPortsFetcher, GetFerryPortsGQL, GetFlightAtNewClassDocument, GetFlightAtNewClassFetcher, GetFlightAtNewClassGQL, GetFlightBrandedFaresFetcher, GetFlightExchangeDetailsDocument, GetFlightExchangeDetailsFetcher, GetFlightExchangeDetailsGQL, GetFlightExtrasOptionsDocument, GetFlightExtrasOptionsFetcher, GetFlightExtrasOptionsGQL, GetFlightFareRulesDocument, GetFlightFareRulesFetcher, GetFlightFareRulesGQL, GetFlightSearchExchangeDocument, GetFlightSearchExchangeFetcher, GetFlightSearchExchangeGQL, GetFlightSeatMapDocument, GetFlightSeatMapFetcher, GetFlightSeatMapGQL, GetFlightUpsellOffersDocument, GetFlightUpsellOffersFetcher, GetFlightUpsellOffersGQL, GetGmtItineraryOptionsDocument, GetGmtItineraryOptionsFetcher, GetGmtItineraryOptionsGQL, GetGutCitySuggestionsDocument, GetGutCitySuggestionsFetcher, GetGutCitySuggestionsGQL, GetGutLocationSuggestionsDocument, GetGutLocationSuggestionsFetcher, GetGutLocationSuggestionsGQL, GetHotelChainsDocument, GetHotelChainsFetcher, GetHotelChainsGQL, GetHotelDetailsDocument, GetHotelDetailsFetcher, GetHotelDetailsGQL, GetIrlDiscountCardsDocument, GetIrlDiscountCardsGQL, GetIrlSupplierStationDocument, GetIrlSupplierStationFetcher, GetIrlSupplierStationGQL, GetLatLonFromHereIdDocument, GetLatLonFromHereIdFetcher, GetLatLonFromHereIdGQL, GetLatestVersionsDocument, GetLatestVersionsFetcher, GetLatestVersionsGQL, GetMIApproversFetcher, GetMiApproversDocument, GetMiApproversGQL, GetMiRequiringMandatoryDefaultValueDocument, GetMiRequiringMandatoryDefaultValueFetcher, GetMiRequiringMandatoryDefaultValueGQL, GetMultipleHotelRatingDocument, GetMultipleHotelRatingFetcher, GetMultipleHotelRatingGQL, GetMultipleHotelsAvailabilityDocument, GetMultipleHotelsAvailabilityGQL, GetOfficeApproversDocument, GetOfficeApproversFetcher, GetOfficeApproversGQL, GetOfficeDivisionsDocument, GetOfficeDivisionsFetcher, GetOfficeDivisionsGQL, GetOfficeDocument, GetOfficeFetcher, GetOfficeGQL, GetOfficeUsersDocument, GetOfficeUsersFetcher, GetOfficeUsersGQL, GetOfficesDocument, GetOfficesFetcher, GetOfficesGQL, GetPriceHiddenSabreExchangeDocument, GetPriceHiddenSabreExchangeFetcher, GetPriceHiddenSabreExchangeGQL, GetRailcardsFetcher as GetRailCardsFetcher, GetRailLiveDeparturesDocument, GetRailLiveDeparturesFetcher, GetRailLiveDeparturesGQL, GetRailProvidersDocument, GetRailProvidersFetcher, GetRailProvidersGQL, GetRailSearchExchangeDocument, GetRailSearchExchangeGQL, GetRailStationDocument, GetRailStationFetcher, GetRailStationGQL, GetRailStationInfoDocument, GetRailStationInfoFetcher, GetRailStationInfoGQL, GetRailcardsDocument, GetRailcardsFetcher, GetRailcardsGQL, GetRedirectApproverDocument, RedirectApproverFetcher as GetRedirectApproverFetcher, GetRedirectApproverGQL, GetRequestedBookingUpdateDocument, GetRequestedBookingUpdateFetcher, GetRequestedBookingUpdateGQL, GetRiskAlertsDocument, GetRiskAlertsFetcher, GetRiskAlertsGQL, GetRouteHappyDocument, GetRouteHappyFetcher, GetRouteHappyGQL, SearchCompanyApproversFetcher as GetSearchCompanyApproversFetcher, GetServiceDocument, GetServiceFetcher, GetServiceGQL, GetTrainSeatMapDocument, GetTrainSeatMapEvolviDocument, GetTrainSeatMapEvolviGQL, GetTrainSeatMapGQL, GetTrainSeatmapEvolviFetcher, GetTrainSeatmapFetcher, GetTrainlineSearchConfigDocument, GetTrainlineSearchConfigFetcher, GetTrainlineSearchConfigGQL, GetUserAddressesDocument, GetUserAddressesGQL, GetUserApproversDocument, GetUserApproversFetcher, GetUserApproversGQL, GetUserBasketCo2InfoDocument, GetUserBasketCo2InfoGQL, GetUserBasketDocument, GetUserBasketGQL, GetUserBasketsDocument, GetUserBasketsFetcher, GetUserBasketsGQL, GetUserCarbonAllowanceDocument, GetUserCarbonAllowanceFetcher, GetUserCarbonAllowanceGQL, GetUserCompanyOfficesDocument, GetUserCompanyOfficesFetcher, GetUserCompanyOfficesGQL, GetUserCarbonAllowanceFetcher as GetUserCurrentCarbonAllowanceFetcher, GetUserDocument, GetUserDocumentLoyaltyDocument, GetUserDocumentLoyaltyFetcher, GetUserDocumentLoyaltyGQL, GetUserEmergencyContactDocument, GetUserEmergencyContactFetcher, GetUserEmergencyContactGQL, GetUserFavouriteSearchesDocument, GetUserFavouriteSearchesFetcher, GetUserFavouriteSearchesGQL, GetUserFavouriteUsersDocument, GetUserFavouriteUsersFetcher, GetUserFavouriteUsersGQL, GetUserFetcher, GetUserGQL, GetUserGuestsDocument, GetUserGuestsFetcher, GetUserGuestsGQL, GetUserMessagesDocument, GetUserMessagesGQL, GetUserMiDefaultValuesDocument, GetUserMiDefaultValuesFetcher, GetUserMiDefaultValuesGQL, GetUserMiDocument, GetUserMiFetcher, GetUserMiGQL, GetUserMiStackDocument, GetUserMiStackFetcher, GetUserMiStackGQL, GetUserPhoneDocument, GetUserPhoneGQL, GetUserPhoneNumbersDocument, GetUserPhoneNumbersGQL, GetUserPreferredTransportHubsDocument, GetUserPreferredTransportHubsFetcher, GetUserPreferredTransportHubsGQL, GetUserRecentBoltSearchesDocument, GetUserRecentBoltSearchesGQL, GetUserRecentSearchesDocument, GetUserRecentSearchesFetcher, GetUserRecentSearchesGQL, GetUserServicesDocument, GetUserServicesGQL, GetUserSharedBasketsDocument, GetUserSharedBasketsFetcher, GetUserSharedBasketsGQL, GetUserUiConfigsDocument, GetUserUiConfigsGQL, GmtItineraryAirportOptionFragmentFragmentDoc, GmtItineraryEurostarOptionFragmentFragmentDoc, GmtItineraryRailStationOptioFragmentFragmentDoc, GroupedFieldsFragmentDoc, HelpInfoFragmentDoc, HelperRoutines, HotelAvalibilityQuoteFetcher, HotelAvalibilityService, HotelQuoteFetcher, HotelStaticTagDisplay, HttpCallService, HttpCancelInterceptor, HttpCancelService, InvokedUserFieldsFragmentDoc, IrlClass, IrlJourneyType, IrlSupplier, IrlTravellerStatus, IsPostcodeValidDocument, IsPostcodeValidGQL, ItineraryEmailTo, LeadPassengerType, LocationType, Location_DetailFragmentDoc, LogLevelType, LogonService, LoungeQuoteFetcher, LoungesInfoFragmentDoc, ManagementInfoDefaultValuePrepopulation, ManagementInfoFieldFragmentDoc, ManagementInfoScope, ManagementInfoSpecialValueSource, ManagementInfoUiPresentation, ManagementInfoValueFieldFragmentDoc, ManagementInfoValueHandling, MarkupFareType, MarkupFlightCabinClass, MarkupFlightSpecialFares, MeetingRoomCatering, MeetingRoomDuration, MeetingRoomEquipment, MeetingRoomLayout, MemoizePipe, MessageTarget, MessageType, MessagesService, ModalOpenerService, MoveItemToADifferentBasketDocument, MoveItemToADifferentBasketGQL, OBTServicesModule, OBTproviders, OfficeFieldsFragmentDoc, OwnerFieldsFragmentDoc, OysterCardsInfoFragmentDoc, ParkingQuoteFetcher, ParkingStationFragmentDoc, PassengerInputType, PassengerType, PaymentMethodFieldsFragmentDoc, PaymentMethodWithCardFieldsFragmentDoc, PhoneType, PlaceCategory, PreferenceKey, ProfileConfigOption, PromptUpdateService, RailAccountsListFragmentDoc, RailCallingPointsFragmentDoc, RailClass, RailFareProcessingService, RailJourneyFareFragmentDoc, RailJourneyOptionFragmentDoc, RailQuoteFetcher, RailResultsStateService, RailSearchComposition, RailSearchJourneyType, RailSeatInfoFragmentDoc, RailSeatPreferenceTypeFragmentDoc, RailSegmentFieldsFragmentDoc, RailStationClassification, RailStoppingPattern, RailTicketFieldsFragmentDoc, RailTicketOptionFieldsFragmentDoc, RailTicketQueueListFragmentDoc, RedirectApproverFetcher, RemoveFavouriteUserDocument, RemoveFavouriteUserGQL, RemoveFavouriteUserUpdater, RemoveItemFromBasketDocument, RemoveItemFromBasketGQL, RemoveItemFromBasketUpdater, RemovePreferredTransportHubDocument, RemovePreferredTransportHubGQL, RemovePreferredTransportHubUpdater, RemoveRedirectApproverDocument, RemoveRedirectApproverGQL, RemoveRedirectApproverUpdater, ResendApproverEmailDocument, ResendApproverEmailFetcher, ResendApproverEmailGQL, ReserveRailSeatsDocument, ReserveRailSeatsGQL, ReserveRailSeatsUpdater, ResultAwareService, RevalidateBasketDocument, RevalidateBasketGQL, RevalidateBasketUpdater, RiskLevel, RoomAvaliabilityFragmentDoc, RouteHappyService, SaveFavouriteSearchDocument, SaveFavouriteSearchGQL, SaveFavouriteSearchUpdater, SaveRecentBoltSearchDocument, SaveRecentBoltSearchGQL, SaveRecentBoltSearchUpdater, SaveRecentSearchDocument, SaveRecentSearchGQL, SaveRecentSearchUpdater, SaveUserAddressDocument, SaveUserAddressGQL, SaveUserAddressUpdater, SearchAirlinesDocument, SearchAirlinesFetcher, SearchAirlinesGQL, SearchAirportsDocument, SearchAirportsFetcher, SearchAirportsGQL, SearchCabHireDocument, SearchCabHireGQL, SearchCarHireDocument, SearchCarHireGQL, SearchCitiesDocument, SearchCitiesGQL, SearchCityFetcher, SearchCompanyApproversDocument, SearchCompanyApproversFetcher, SearchCompanyApproversGQL, SearchCompanyUsersDocument, SearchCompanyUsersFetcher, SearchCompanyUsersGQL, SearchDepotFetcher, SearchDiscoverLocationDocument, SearchDiscoverLocationFetcher, SearchDiscoverLocationGQL, SearchDocumentValidationService, SearchEurostarDocument, SearchEurostarGQL, SearchFastTrackDocument, SearchFastTrackGQL, SearchFlightsV2Document, SearchFlightsV2GQL, SearchGeoLocationDocument, SearchGeoLocationFetcher, SearchGeoLocationGQL, SearchHotelChainsDocument, SearchHotelChainsFetcher, SearchHotelChainsGQL, SearchHotelsDocument, SearchHotelsGQL, SearchIrlDocument, SearchIrlGQL, SearchIrlStationsFetcher, SearchIrlSupplierStationsDocument, SearchIrlSupplierStationsGQL, SearchLoungesDocument, SearchLoungesGQL, SearchMIAutoSuggestValuesFetcher, SearchMiAutoSuggestValuesDocument, SearchMiAutoSuggestValuesGQL, SearchParkingDocument, SearchParkingGQL, SearchPaymentValidationService, SearchPostcodeFetcher, SearchPostcodesDocument, SearchPostcodesGQL, SearchRailDocument, SearchRailGQL, SearchRailInwardDocument, SearchRailInwardFetcher, SearchRailInwardGQL, SearchRailStationsDocument, SearchRailStationsFetcher, SearchRailStationsGQL, SearchUserAddressDocument, SearchUserAddressGQL, SearchUserCanBookForDocument, SearchUserCanBookForGQL, SearchUserWithEmailDocument, SearchUserWithEmailFetcher, SearchUserWithEmailGQL, SearchUsersCanBookForFetcher, SeasonTicketCardType, SeasonTicketClassType, SeasonTicketDeliveryType, SeasonTicketLondonUnderground, SeasonTicketType, SelectBasketApproverDocument, SelectBasketApproverGQL, SelectBasketApproverUpdater, SelectBasketMultiLevelApproversDocument, SelectBasketMultiLevelApproversGQL, SelectBasketMultiLevelApproversUpdater, SelectBasketNotifyApproverDocument, SelectBasketNotifyApproverGQL, SelectBasketNotifyApproverUpdater, SendBackToQueueDocument, SendBackToQueueGQL, SendBackToQueueUpdater, SendOfflineNotificationDocument, SendOfflineNotificationFetcher, SendOfflineNotificationGQL, ServiceMinimalFieldsFragmentDoc, ServiceType, SetBasketItemApexxTokenDocument, SetBasketItemApexxTokenGQL, SetBasketItemApexxTokenUpdater, SetBasketItemLeadPassengerDocument, SetBasketItemLeadPassengerGQL, SetBasketItemPaymentMethodDocument, SetBasketItemPaymentMethodGQL, SetEmptyUserMIDefaultValueUpdater, SetEmptyUserMiDefaultValueDocument, SetEmptyUserMiDefaultValueGQL, SetMIValuesUpdater, SetMiValuesDocument, SetMiValuesGQL, SetPaymentOptionUpdater, SetPreferredTransportHubDocument, SetPreferredTransportHubGQL, SetPreferredTransportHubUpdater, SetUserLanguageDocument, SetUserLanguageGQL, SetUserMIDefaultValueUpdater, SetUserMiDefaultValueDocument, SetUserMiDefaultValueGQL, ShareBasketDocument, ShareBasketGQL, ShareBasketUpdater, ShoppingInfoFragmentDoc, SmartCardsInfoFragmentDoc, SplitRailJourneyFareFragmentDoc, StorageService, SuggestPlacesDocument, SuggestPlacesFetcher, SuggestPlacesGQL, TicketOfficeInfoFragmentDoc, TicketQueueService, TicketType, TimeType, ToiletsBabyInfoFragmentDoc, TrainlineTenantIsFrenchDocument, TrainlineTenantIsFrenchFetcher, TrainlineTenantIsFrenchGQL, TransportType, TravellerGuestsFieldsFragmentDoc, TravellerUserFieldsFragmentDoc, UiConfigTarget, UpdateBasketNotesDocument, UpdateBasketNotesGQL, UpdateBasketTitleDocument, UpdateBasketTitleGQL, UpdateDOBUpdater, UpdateDobDocument, UpdateDobGQL, UpdateExchangeBasketDocument, UpdateExchangeBasketGQL, UpdateExchangeBasketUpdater, UpdateFlightSeatMapDocument, UpdateFlightSeatMapGQL, UpdateFlightSeatMapUpdater, UserApproversFieldsFragmentDoc, UserFieldsFragmentDoc, UserMessagesFetcher, UserMiFragmentDoc, UserMinimalFieldsFragmentDoc, UserProductsFetcher, UserService, UserSummaryFieldsFragmentDoc, UserUiConfigsFetcher, UsersFragmentDoc, ValidateBasketItemMiDocument, ValidateBasketItemMiFetcher, ValidateBasketItemMiGQL, ValidateBasketMiDocument, ValidateBasketMiFetcher, ValidateBasketMiGQL, VehicleType, WebTokenService, WifiInfoFragmentDoc, WithSubscriptionComponent };
|
|
20703
|
+
export { AcceptNewPriceDocument, AcceptNewPriceGQL, AcceptNewPriceUpdater, AcceptProcessTermsDocument, AcceptProcessTermsGQL, AccessibilityInfoFragmentDoc, AddBasketItemCustomRemarkDocument, AddBasketItemCustomRemarkGQL, AddBasketItemCustomRemarkUpdater, AddBasketItemMarkupDocument, AddBasketItemMarkupGQL, AddBasketItemMarkupUpdater, AddFavouriteUserDocument, AddFavouriteUserGQL, AddFavouriteUserUpdater, AddGuestToBasketItemUpdater, AddGuestsToBasketItemDocument, AddGuestsToBasketItemGQL, AddItemToBasketDocument, AddItemToBasketGQL, AddItemToBasketUpdater, AddRedirectApproverDocument, AddRedirectApproverGQL, AddRedirectApproverUpdater, AddToBasketFieldsFragmentDoc, AddUpdatedItemToBasketDocument, AddUpdatedItemToBasketGQL, AddUpdatedItemToBasketUpdater, AddUserToBasketItemUpdater, AddUsersToBasketItemDocument, AddUsersToBasketItemGQL, AddressType, AmendBookingDocument, AmendBookingGQL, AmendBookingUpdater, ApexxCardStatus, ApexxCreateCardDocument, ApexxCreateCardGQL, ApexxDeleteCardDocument, ApexxDeleteCardGQL, ApexxDeleteCardUpdater, ApexxListCardsDocument, ApexxListCardsGQL, ApexxUpdateCardDocument, ApexxUpdateCardGQL, ApexxUpdateCardUpdater, ApplyJitFlightRulesDocument, ApplyJitFlightRulesGQL, ApplyJitHotelRulesDocument, ApplyJitHotelRulesGQL, ApprovalEventType, ApproverUserFieldsFragmentDoc, AuthHttpService, BaggageType, BannerDisplayTarget, BasketFieldsFragmentDoc, BasketItemStatus, BasketMinimalFieldsFragmentDoc, BasketPanelComponent, BasketStatus, BeforeAmendCabSearchDocument, BeforeAmendCabSearchFetcher, BeforeAmendCabSearchGQL, BeforeAmendCarHireSearchDocument, BeforeAmendCarHireSearchFetcher, BeforeAmendCarHireSearchGQL, BookBasketDocument, BookBasketFieldsFragmentDoc, BookBasketGQL, BookBasketUpdater, BookerType, CabHireVehicleType, CabhireQuoteFetcher, CanAmendBookingDocument, CanAmendBookingFetcher, CanAmendBookingGQL, CancelBookingDocument, CancelBookingGQL, CancelBookingUpdater, CarHireAvailabilityDetailFetcher, CarHireAvailabilityDetailRequestDocument, CarHireAvailabilityDetailRequestGQL, CarHireClass, CarHireTransmission, CarHireType, CarbonPolicyDuration, CarbonPolicySource, CarhireQuoteFetcher, ChangeBasketOwnershipDocument, ChangeBasketOwnershipGQL, ChangeBasketOwnershipUpdater, ChangeMode, ChatbotEurostarSearchService, ChatbotFlightSearchService, ChatbotHotelSearchService, ChatbotRailSearchService, ChatbotSearchDispatcherService, ChatbotService, CheckForDuplicateBookingsDocument, CheckForDuplicateBookingsFetcher, CheckForDuplicateBookingsGQL, CheckIfBasketRequiresApprovalDocument, CheckIfBasketRequiresApprovalFetcher, CheckIfBasketRequiresApprovalGQL, CompanyType, ConfirmMessagesDocument, ConfirmMessagesGQL, ConvertCurrencyDocument, ConvertCurrencyFetcher, ConvertCurrencyGQL, CreateBasketDocument, CreateBasketFieldsFragmentDoc, CreateBasketFromDraftDocument, CreateBasketFromDraftGQL, CreateBasketFromDraftUpdater, CreateBasketGQL, CreateBasketNoteDocument, CreateBasketNoteGQL, CreateBasketNoteUpdater, CreateBasketUpdater, CreateGuestDocument, CreateGuestGQL, CreateGuestUpdater, CreateItineraryExchangeDocument, CreateItineraryExchangeGQL, CurrencyCode, CycleInfoFragmentDoc, DeleteAllRecentSearchesDocument, DeleteAllRecentSearchesGQL, DeleteAllRecentSearchesUpdater, DeleteBasketDocument, DeleteBasketGQL, DeleteBasketUpdater, DeleteFavouriteSearchDocument, DeleteFavouriteSearchGQL, DeleteFavouriteSearchUpdater, DeleteGuestDocument, DeleteGuestGQL, DeleteGuestUpdater, DeleteRecentSearchDocument, DeleteRecentSearchGQL, DeleteUserAddressDocument, DeleteUserAddressGQL, DiscountType, DistanceTypes, DivisionFieldsFragmentDoc, DocGender, DocType, DownloadETicketDocument, DownloadETicketFetcher, DownloadETicketGQL, DraftBasketType, EditUserAddressDocument, EditUserAddressGQL, EditUserAddressUpdater, EditUserDocument, EditUserGQL, EmailAmbulanceBookingFetcher, EmailAmbulanceBookingReqDocument, EmailAmbulanceBookingReqGQL, EmailApartmentBookingFetcher, EmailApartmentBookingReqDocument, EmailApartmentBookingReqGQL, EmailBasketDocument, EmailBasketFetcher, EmailBasketGQL, EmailFerryBookingFetcher, EmailFerryBookingReqDocument, EmailFerryBookingReqGQL, EmailMeetingRoomBookingFetcher, EmailMeetingRoomBookingReqDocument, EmailMeetingRoomBookingReqGQL, EmailSeasonTicketBookingFetcher, EmailSeasonTicketBookingReqDocument, EmailSeasonTicketBookingReqGQL, EntLocationsByCityDocument, EntLocationsByCityGQL, EntLocationsByPostcodeDocument, EntLocationsByPostcodeGQL, EnterpriseBasketService, EnterpriseMyBookingsService, EnterpriseSearchService, Environment, ErrorsFragmentDoc, EurostarQuoteFetcher, EventMessenager, EvolviSeatmapsAreEnabledDocument, EvolviSeatmapsAreEnabledGQL, ExchangeType, ExternalHttpService, FailedEmail, FareRuleType, FareType, FerryOrEurotunnel, FlightCabinClass, FlightItineraryFieldsFragmentDoc, FlightPassengerType, FlightQuoteFetcher, GenerateBasketPdfDocument, GenerateBasketPdfFetcher, GenerateBasketPdfGQL, GetAirAvailabilityDocument, GetAirAvailabilityFetcher, GetAirAvailabilityGQL, GetAllAirlinesDocument, GetAllAirlinesFetcher, GetAllAirlinesGQL, GetApexxListCardsFetcher, GetBannerDocument, GetBannerFetcher, GetBannerGQL, GetBasketApprovalInfoDocument, GetBasketApprovalInfoFetcher, GetBasketApprovalInfoGQL, GetBasketApprovalTimestampsDocument, GetBasketApprovalTimestampsGQL, GetBasketCo2InfoFetcher, GetBasketFetcher, GetBasketItemCustomRemarksDocument, GetBasketItemCustomRemarksFetcher, GetBasketItemCustomRemarksGQL, GetBasketNotesDocument, GetBasketNotesFetcher, GetBasketNotesGQL, GetBasketQuoteDocument, GetBasketQuoteFetcher, GetBasketQuoteGQL, GetBrandedFaresDocument, GetBrandedFaresGQL, GetCancellationInfoDocument, GetCancellationInfoFetcher, GetCancellationInfoGQL, GetCarHireDepotsDocument, GetCarHireDepotsGQL, GetCarHireProvidersDocument, GetCarHireProvidersFetcher, GetCarHireProvidersGQL, GetCompaniesDocument, GetCompaniesFetcher, GetCompaniesGQL, GetCompanyDocument, GetCompanyFetcher, GetCompanyGQL, GetConfermaQuicklistDocument, GetConfermaQuicklistGQL, GetConfermaRoomImagesDocument, GetConfermaRoomImagesGQL, GetCovidMicrositeTokenDocument, GetCovidMicrositeTokenFetcher, GetCovidMicrositeTokenGQL, GetCurrencyConversionRatesDocument, GetCurrencyConversionRatesFetcher, GetCurrencyConversionRatesGQL, GetDivisionDocument, GetDivisionGQL, GetDraftBasketsDocument, GetDraftBasketsFetcher, GetDraftBasketsGQL, GetEmptyUserMiDefaultValuesDocument, GetEmptyUserMiDefaultValuesFetcher, GetEmptyUserMiDefaultValuesGQL, GetEntLocationByPostcode, GetEvolviSeatmapsAreEnabledFetcher, GetFerryPortsDocument, GetFerryPortsFetcher, GetFerryPortsGQL, GetFlightAtNewClassDocument, GetFlightAtNewClassFetcher, GetFlightAtNewClassGQL, GetFlightBrandedFaresFetcher, GetFlightExchangeDetailsDocument, GetFlightExchangeDetailsFetcher, GetFlightExchangeDetailsGQL, GetFlightExtrasOptionsDocument, GetFlightExtrasOptionsFetcher, GetFlightExtrasOptionsGQL, GetFlightFareRulesDocument, GetFlightFareRulesFetcher, GetFlightFareRulesGQL, GetFlightSearchExchangeDocument, GetFlightSearchExchangeFetcher, GetFlightSearchExchangeGQL, GetFlightSeatMapDocument, GetFlightSeatMapFetcher, GetFlightSeatMapGQL, GetFlightUpsellOffersDocument, GetFlightUpsellOffersFetcher, GetFlightUpsellOffersGQL, GetGmtItineraryOptionsDocument, GetGmtItineraryOptionsFetcher, GetGmtItineraryOptionsGQL, GetGutCitySuggestionsDocument, GetGutCitySuggestionsFetcher, GetGutCitySuggestionsGQL, GetGutLocationSuggestionsDocument, GetGutLocationSuggestionsFetcher, GetGutLocationSuggestionsGQL, GetHotelChainsDocument, GetHotelChainsFetcher, GetHotelChainsGQL, GetHotelDetailsDocument, GetHotelDetailsFetcher, GetHotelDetailsGQL, GetIrlDiscountCardsDocument, GetIrlDiscountCardsGQL, GetIrlSupplierStationDocument, GetIrlSupplierStationFetcher, GetIrlSupplierStationGQL, GetJyrneyUrlDocument, GetJyrneyUrlFetcher, GetJyrneyUrlGQL, GetLatLonFromHereIdDocument, GetLatLonFromHereIdFetcher, GetLatLonFromHereIdGQL, GetLatestVersionsDocument, GetLatestVersionsFetcher, GetLatestVersionsGQL, GetMIApproversFetcher, GetMiApproversDocument, GetMiApproversGQL, GetMiRequiringMandatoryDefaultValueDocument, GetMiRequiringMandatoryDefaultValueFetcher, GetMiRequiringMandatoryDefaultValueGQL, GetMultipleHotelRatingDocument, GetMultipleHotelRatingFetcher, GetMultipleHotelRatingGQL, GetMultipleHotelsAvailabilityDocument, GetMultipleHotelsAvailabilityGQL, GetOfficeApproversDocument, GetOfficeApproversFetcher, GetOfficeApproversGQL, GetOfficeDivisionsDocument, GetOfficeDivisionsFetcher, GetOfficeDivisionsGQL, GetOfficeDocument, GetOfficeFetcher, GetOfficeGQL, GetOfficeUsersDocument, GetOfficeUsersFetcher, GetOfficeUsersGQL, GetOfficesDocument, GetOfficesFetcher, GetOfficesGQL, GetPriceHiddenSabreExchangeDocument, GetPriceHiddenSabreExchangeFetcher, GetPriceHiddenSabreExchangeGQL, GetProcessTermsPriceDocument, GetProcessTermsPriceGQL, GetRailcardsFetcher as GetRailCardsFetcher, GetRailLiveDeparturesDocument, GetRailLiveDeparturesFetcher, GetRailLiveDeparturesGQL, GetRailProvidersDocument, GetRailProvidersFetcher, GetRailProvidersGQL, GetRailSearchExchangeDocument, GetRailSearchExchangeGQL, GetRailStationDocument, GetRailStationFetcher, GetRailStationGQL, GetRailStationInfoDocument, GetRailStationInfoFetcher, GetRailStationInfoGQL, GetRailcardsDocument, GetRailcardsFetcher, GetRailcardsGQL, GetRedirectApproverDocument, RedirectApproverFetcher as GetRedirectApproverFetcher, GetRedirectApproverGQL, GetRequestedBookingUpdateDocument, GetRequestedBookingUpdateFetcher, GetRequestedBookingUpdateGQL, GetRiskAlertsDocument, GetRiskAlertsFetcher, GetRiskAlertsGQL, GetRouteHappyDocument, GetRouteHappyFetcher, GetRouteHappyGQL, SearchCompanyApproversFetcher as GetSearchCompanyApproversFetcher, GetServiceDocument, GetServiceFetcher, GetServiceGQL, GetTrainSeatMapDocument, GetTrainSeatMapEvolviDocument, GetTrainSeatMapEvolviGQL, GetTrainSeatMapGQL, GetTrainSeatmapEvolviFetcher, GetTrainSeatmapFetcher, GetTrainlineSearchConfigDocument, GetTrainlineSearchConfigFetcher, GetTrainlineSearchConfigGQL, GetUserAddressesDocument, GetUserAddressesGQL, GetUserApproversDocument, GetUserApproversFetcher, GetUserApproversGQL, GetUserBasketCo2InfoDocument, GetUserBasketCo2InfoGQL, GetUserBasketDocument, GetUserBasketGQL, GetUserBasketsDocument, GetUserBasketsFetcher, GetUserBasketsGQL, GetUserCarbonAllowanceDocument, GetUserCarbonAllowanceFetcher, GetUserCarbonAllowanceGQL, GetUserCompanyOfficesDocument, GetUserCompanyOfficesFetcher, GetUserCompanyOfficesGQL, GetUserCarbonAllowanceFetcher as GetUserCurrentCarbonAllowanceFetcher, GetUserDocument, GetUserDocumentLoyaltyDocument, GetUserDocumentLoyaltyFetcher, GetUserDocumentLoyaltyGQL, GetUserEmergencyContactDocument, GetUserEmergencyContactFetcher, GetUserEmergencyContactGQL, GetUserFavouriteSearchesDocument, GetUserFavouriteSearchesFetcher, GetUserFavouriteSearchesGQL, GetUserFavouriteUsersDocument, GetUserFavouriteUsersFetcher, GetUserFavouriteUsersGQL, GetUserFetcher, GetUserGQL, GetUserGuestsDocument, GetUserGuestsFetcher, GetUserGuestsGQL, GetUserMessagesDocument, GetUserMessagesGQL, GetUserMiDefaultValuesDocument, GetUserMiDefaultValuesFetcher, GetUserMiDefaultValuesGQL, GetUserMiDocument, GetUserMiFetcher, GetUserMiGQL, GetUserMiStackDocument, GetUserMiStackFetcher, GetUserMiStackGQL, GetUserPhoneDocument, GetUserPhoneGQL, GetUserPhoneNumbersDocument, GetUserPhoneNumbersGQL, GetUserPreferredTransportHubsDocument, GetUserPreferredTransportHubsFetcher, GetUserPreferredTransportHubsGQL, GetUserRecentBoltSearchesDocument, GetUserRecentBoltSearchesGQL, GetUserRecentSearchesDocument, GetUserRecentSearchesFetcher, GetUserRecentSearchesGQL, GetUserServicesDocument, GetUserServicesGQL, GetUserSharedBasketsDocument, GetUserSharedBasketsFetcher, GetUserSharedBasketsGQL, GetUserUiConfigsDocument, GetUserUiConfigsGQL, GmtItineraryAirportOptionFragmentFragmentDoc, GmtItineraryEurostarOptionFragmentFragmentDoc, GmtItineraryRailStationOptioFragmentFragmentDoc, GroupedFieldsFragmentDoc, HelpInfoFragmentDoc, HelperRoutines, HotelAvalibilityQuoteFetcher, HotelAvalibilityService, HotelQuoteFetcher, HotelStaticTagDisplay, HttpCallService, HttpCancelInterceptor, HttpCancelService, InvokedUserFieldsFragmentDoc, IrlClass, IrlJourneyType, IrlSupplier, IrlTravellerStatus, IsPostcodeValidDocument, IsPostcodeValidGQL, ItineraryEmailTo, LeadPassengerType, LocationType, Location_DetailFragmentDoc, LogLevelType, LogonService, LoungeQuoteFetcher, LoungesInfoFragmentDoc, ManagementInfoDefaultValuePrepopulation, ManagementInfoFieldFragmentDoc, ManagementInfoScope, ManagementInfoSpecialValueSource, ManagementInfoUiPresentation, ManagementInfoValueFieldFragmentDoc, ManagementInfoValueHandling, MarkupFareType, MarkupFlightCabinClass, MarkupFlightSpecialFares, MeetingRoomCatering, MeetingRoomDuration, MeetingRoomEquipment, MeetingRoomLayout, MemoizePipe, MessageTarget, MessageType, MessagesService, ModalOpenerService, MoveItemToADifferentBasketDocument, MoveItemToADifferentBasketGQL, OBTServicesModule, OBTproviders, OfficeFieldsFragmentDoc, OwnerFieldsFragmentDoc, OysterCardsInfoFragmentDoc, ParkingQuoteFetcher, ParkingStationFragmentDoc, PassengerInputType, PassengerType, PaymentMethodFieldsFragmentDoc, PaymentMethodWithCardFieldsFragmentDoc, PhoneType, PlaceCategory, PreferenceKey, ProfileConfigOption, PromptUpdateService, RailAccountsListFragmentDoc, RailCallingPointsFragmentDoc, RailClass, RailFareProcessingService, RailJourneyFareFragmentDoc, RailJourneyOptionFragmentDoc, RailQuoteFetcher, RailResultsStateService, RailSearchComposition, RailSearchJourneyType, RailSeatInfoFragmentDoc, RailSeatPreferenceTypeFragmentDoc, RailSegmentFieldsFragmentDoc, RailStationClassification, RailStoppingPattern, RailTicketFieldsFragmentDoc, RailTicketOptionFieldsFragmentDoc, RailTicketQueueListFragmentDoc, RedirectApproverFetcher, RemoveFavouriteUserDocument, RemoveFavouriteUserGQL, RemoveFavouriteUserUpdater, RemoveItemFromBasketDocument, RemoveItemFromBasketGQL, RemoveItemFromBasketUpdater, RemovePreferredTransportHubDocument, RemovePreferredTransportHubGQL, RemovePreferredTransportHubUpdater, RemoveRedirectApproverDocument, RemoveRedirectApproverGQL, RemoveRedirectApproverUpdater, ResendApproverEmailDocument, ResendApproverEmailFetcher, ResendApproverEmailGQL, ReserveRailSeatsDocument, ReserveRailSeatsGQL, ReserveRailSeatsUpdater, ResultAwareService, RevalidateBasketDocument, RevalidateBasketGQL, RevalidateBasketUpdater, RiskLevel, RoomAvaliabilityFragmentDoc, RouteHappyService, SaveFavouriteSearchDocument, SaveFavouriteSearchGQL, SaveFavouriteSearchUpdater, SaveRecentBoltSearchDocument, SaveRecentBoltSearchGQL, SaveRecentBoltSearchUpdater, SaveRecentSearchDocument, SaveRecentSearchGQL, SaveRecentSearchUpdater, SaveUserAddressDocument, SaveUserAddressGQL, SaveUserAddressUpdater, SearchAirlinesDocument, SearchAirlinesFetcher, SearchAirlinesGQL, SearchAirportsDocument, SearchAirportsFetcher, SearchAirportsGQL, SearchCabHireDocument, SearchCabHireGQL, SearchCarHireDocument, SearchCarHireGQL, SearchCitiesDocument, SearchCitiesGQL, SearchCityFetcher, SearchCompanyApproversDocument, SearchCompanyApproversFetcher, SearchCompanyApproversGQL, SearchCompanyUsersDocument, SearchCompanyUsersFetcher, SearchCompanyUsersGQL, SearchDepotFetcher, SearchDiscoverLocationDocument, SearchDiscoverLocationFetcher, SearchDiscoverLocationGQL, SearchDocumentValidationService, SearchEurostarDocument, SearchEurostarGQL, SearchFastTrackDocument, SearchFastTrackGQL, SearchFlightsV2Document, SearchFlightsV2GQL, SearchGeoLocationDocument, SearchGeoLocationFetcher, SearchGeoLocationGQL, SearchHotelChainsDocument, SearchHotelChainsFetcher, SearchHotelChainsGQL, SearchHotelsDocument, SearchHotelsGQL, SearchIrlDocument, SearchIrlGQL, SearchIrlStationsFetcher, SearchIrlSupplierStationsDocument, SearchIrlSupplierStationsGQL, SearchLoungesDocument, SearchLoungesGQL, SearchMIAutoSuggestValuesFetcher, SearchMiAutoSuggestValuesDocument, SearchMiAutoSuggestValuesGQL, SearchParkingDocument, SearchParkingGQL, SearchPaymentValidationService, SearchPostcodeFetcher, SearchPostcodesDocument, SearchPostcodesGQL, SearchRailDocument, SearchRailGQL, SearchRailInwardDocument, SearchRailInwardFetcher, SearchRailInwardGQL, SearchRailStationsDocument, SearchRailStationsFetcher, SearchRailStationsGQL, SearchUserAddressDocument, SearchUserAddressGQL, SearchUserCanBookForDocument, SearchUserCanBookForGQL, SearchUserWithEmailDocument, SearchUserWithEmailFetcher, SearchUserWithEmailGQL, SearchUsersCanBookForFetcher, SeasonTicketCardType, SeasonTicketClassType, SeasonTicketDeliveryType, SeasonTicketLondonUnderground, SeasonTicketType, SelectBasketApproverDocument, SelectBasketApproverGQL, SelectBasketApproverUpdater, SelectBasketMultiLevelApproversDocument, SelectBasketMultiLevelApproversGQL, SelectBasketMultiLevelApproversUpdater, SelectBasketNotifyApproverDocument, SelectBasketNotifyApproverGQL, SelectBasketNotifyApproverUpdater, SendBackToQueueDocument, SendBackToQueueGQL, SendBackToQueueUpdater, SendOfflineNotificationDocument, SendOfflineNotificationFetcher, SendOfflineNotificationGQL, ServiceMinimalFieldsFragmentDoc, ServiceType, SetBasketItemApexxTokenDocument, SetBasketItemApexxTokenGQL, SetBasketItemApexxTokenUpdater, SetBasketItemLeadPassengerDocument, SetBasketItemLeadPassengerGQL, SetBasketItemPaymentMethodDocument, SetBasketItemPaymentMethodGQL, SetEmptyUserMIDefaultValueUpdater, SetEmptyUserMiDefaultValueDocument, SetEmptyUserMiDefaultValueGQL, SetMIValuesUpdater, SetMiValuesDocument, SetMiValuesGQL, SetPaymentOptionUpdater, SetPreferredTransportHubDocument, SetPreferredTransportHubGQL, SetPreferredTransportHubUpdater, SetUserLanguageDocument, SetUserLanguageGQL, SetUserMIDefaultValueUpdater, SetUserMiDefaultValueDocument, SetUserMiDefaultValueGQL, ShareBasketDocument, ShareBasketGQL, ShareBasketUpdater, ShoppingInfoFragmentDoc, SmartCardsInfoFragmentDoc, SplitRailJourneyFareFragmentDoc, StorageService, SuggestPlacesDocument, SuggestPlacesFetcher, SuggestPlacesGQL, TicketOfficeInfoFragmentDoc, TicketQueueService, TicketType, TimeType, ToiletsBabyInfoFragmentDoc, TrainlineTenantIsFrenchDocument, TrainlineTenantIsFrenchFetcher, TrainlineTenantIsFrenchGQL, TransportType, TravellerGuestsFieldsFragmentDoc, TravellerUserFieldsFragmentDoc, UiConfigTarget, UpdateBasketNotesDocument, UpdateBasketNotesGQL, UpdateBasketTitleDocument, UpdateBasketTitleGQL, UpdateDOBUpdater, UpdateDobDocument, UpdateDobGQL, UpdateExchangeBasketDocument, UpdateExchangeBasketGQL, UpdateExchangeBasketUpdater, UpdateFlightSeatMapDocument, UpdateFlightSeatMapGQL, UpdateFlightSeatMapUpdater, UserApproversFieldsFragmentDoc, UserFieldsFragmentDoc, UserMessagesFetcher, UserMiFragmentDoc, UserMinimalFieldsFragmentDoc, UserProductsFetcher, UserService, UserSummaryFieldsFragmentDoc, UserUiConfigsFetcher, UsersFragmentDoc, ValidateBasketItemMiDocument, ValidateBasketItemMiFetcher, ValidateBasketItemMiGQL, ValidateBasketMiDocument, ValidateBasketMiFetcher, ValidateBasketMiGQL, VehicleType, WebTokenService, WifiInfoFragmentDoc, WithSubscriptionComponent };
|
|
18113
20704
|
//# sourceMappingURL=sabstravtech-obtservices-angular.mjs.map
|