@qite/tide-booking-component 1.4.103 → 1.4.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/build/build-cjs/index.js +2167 -1400
  2. package/build/build-cjs/src/search-results/components/filters/filters.d.ts +2 -0
  3. package/build/build-cjs/src/search-results/components/hotel/hotel-accommodation-results.d.ts +1 -0
  4. package/build/build-cjs/src/search-results/store/search-results-selectors.d.ts +424 -0
  5. package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +27 -8
  6. package/build/build-cjs/src/search-results/types.d.ts +14 -2
  7. package/build/build-cjs/src/search-results/utils/search-results-utils.d.ts +8 -6
  8. package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +3 -3
  9. package/build/build-cjs/src/shared/components/flyin/packaging-flights-flyin.d.ts +7 -0
  10. package/build/build-esm/index.js +2152 -1395
  11. package/build/build-esm/src/search-results/components/filters/filters.d.ts +2 -0
  12. package/build/build-esm/src/search-results/components/hotel/hotel-accommodation-results.d.ts +1 -0
  13. package/build/build-esm/src/search-results/store/search-results-selectors.d.ts +424 -0
  14. package/build/build-esm/src/search-results/store/search-results-slice.d.ts +27 -8
  15. package/build/build-esm/src/search-results/types.d.ts +14 -2
  16. package/build/build-esm/src/search-results/utils/search-results-utils.d.ts +8 -6
  17. package/build/build-esm/src/shared/components/flyin/flyin.d.ts +3 -3
  18. package/build/build-esm/src/shared/components/flyin/packaging-flights-flyin.d.ts +7 -0
  19. package/package.json +1 -1
  20. package/src/booking-wizard/features/flight-options/index.tsx +6 -2
  21. package/src/search-results/components/filters/filters.tsx +8 -9
  22. package/src/search-results/components/hotel/hotel-accommodation-results.tsx +81 -24
  23. package/src/search-results/components/search-results-container/search-results-container.tsx +118 -102
  24. package/src/search-results/store/search-results-selectors.ts +73 -0
  25. package/src/search-results/store/search-results-slice.ts +94 -14
  26. package/src/search-results/types.ts +14 -2
  27. package/src/search-results/utils/search-results-utils.ts +310 -58
  28. package/src/shared/components/flyin/flyin.tsx +102 -19
  29. package/src/shared/components/flyin/packaging-flights-flyin.tsx +164 -0
  30. package/styles/components/_flyin.scss +16 -0
  31. package/styles/components/_search.scss +4 -1
@@ -1,9 +1,7 @@
1
- import React, { useContext, useEffect, useState } from 'react';
1
+ import React, { useContext, useState } from 'react';
2
2
  import { Filter, FilterOption } from '../../types';
3
3
  import MultiRangeFilter from '../multi-range-filter';
4
4
  import SearchResultsConfigurationContext from '../../search-results-configuration-context';
5
- import { resetFilters, setFilters } from '../../store/search-results-slice';
6
- import { useDispatch } from 'react-redux';
7
5
  import Spinner from '../spinner/spinner';
8
6
  import Icon from '../icon';
9
7
  import { getTranslations } from '../../../shared/utils/localization-util';
@@ -14,10 +12,13 @@ interface FiltersProps {
14
12
  isOpen: boolean;
15
13
  handleSetIsOpen: () => void;
16
14
  isLoading?: boolean;
15
+ setFilters: (filters: Filter[]) => void;
16
+ resetFilters: (filters: Filter[]) => void;
17
17
  }
18
18
 
19
- const Filters: React.FC<FiltersProps> = ({ initialFilters, filters, isOpen, handleSetIsOpen, isLoading }) => {
19
+ const Filters: React.FC<FiltersProps> = ({ initialFilters, filters, isOpen, handleSetIsOpen, isLoading, setFilters, resetFilters }) => {
20
20
  const context = useContext(SearchResultsConfigurationContext);
21
+
21
22
  if (!context || !context.showFilters) {
22
23
  return null;
23
24
  }
@@ -25,8 +26,6 @@ const Filters: React.FC<FiltersProps> = ({ initialFilters, filters, isOpen, hand
25
26
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
26
27
  const [visibleFilters, setVisibleFilters] = useState<Record<string, boolean>>({});
27
28
 
28
- const dispatch = useDispatch();
29
-
30
29
  const toggleFilterVisibility = (filterId: string) => {
31
30
  setVisibleFilters((prev) => ({
32
31
  ...prev,
@@ -44,7 +43,7 @@ const Filters: React.FC<FiltersProps> = ({ initialFilters, filters, isOpen, hand
44
43
  };
45
44
  });
46
45
 
47
- dispatch(setFilters(updated));
46
+ setFilters(updated);
48
47
  };
49
48
 
50
49
  const handleSliderChange = (filter: Filter, newMin: number, newMax: number) => {
@@ -58,12 +57,12 @@ const Filters: React.FC<FiltersProps> = ({ initialFilters, filters, isOpen, hand
58
57
  };
59
58
  });
60
59
 
61
- dispatch(setFilters(updated));
60
+ setFilters(updated);
62
61
  };
63
62
 
64
63
  const handleFullReset = () => {
65
64
  if (!isLoading) {
66
- dispatch(resetFilters(initialFilters));
65
+ resetFilters(initialFilters);
67
66
  }
68
67
  };
69
68
 
@@ -1,8 +1,8 @@
1
- import React, { useContext } from 'react';
2
- import { HotelResult, SearchResultsConfiguration } from '../../types';
1
+ import React, { useContext, useState } from 'react';
2
+ import { FlyInType, HotelResult, SearchResultsConfiguration } from '../../types';
3
3
  import Spinner from '../spinner/spinner';
4
4
  import HotelCard from './hotel-card';
5
- import { useSelector } from 'react-redux';
5
+ import { useDispatch, useSelector } from 'react-redux';
6
6
  import { SearchResultsRootState } from '../../store/search-results-store';
7
7
  import { BookingPackageItem, PackagingAccommodationResponse, PortalQsmType } from '@qite/tide-client';
8
8
  import { format, parseISO } from 'date-fns';
@@ -10,9 +10,11 @@ import { calculateNights, formatPrice, getTranslations } from '../../../shared/u
10
10
  import SearchResultsConfigurationContext from '../../search-results-configuration-context';
11
11
  import { first } from 'lodash';
12
12
  import Icon from '../icon';
13
+ import { setFlyInIsOpen, setFlyInType } from '../../store/search-results-slice';
13
14
 
14
15
  interface HotelAccommodationResultsProps {
15
16
  isLoading: boolean;
17
+ isFlyIn?: boolean;
16
18
  }
17
19
 
18
20
  const getLocation = (locationName?: string, regionName?: string, countryName?: string, fallback?: string): string => {
@@ -71,7 +73,14 @@ const mapPackagingAccoResult = (searchResult: PackagingAccommodationResponse, cm
71
73
  };
72
74
  };
73
75
 
74
- const renderHotelResults = (results: HotelResult[], context: SearchResultsConfiguration, activeTab: string | null, translations: any) => {
76
+ const renderHotelResults = (
77
+ results: HotelResult[],
78
+ context: SearchResultsConfiguration,
79
+ activeTab: string | null,
80
+ translations: any,
81
+ selectedPackagingAccoResult?: HotelResult,
82
+ isFlyIn?: boolean
83
+ ) => {
75
84
  const renderedResults = results.map((result, index) => {
76
85
  const key = `${result.id ?? result.code}-${index}`;
77
86
 
@@ -86,19 +95,28 @@ const renderHotelResults = (results: HotelResult[], context: SearchResultsConfig
86
95
  return <HotelCard key={key} result={result} translations={translations} />;
87
96
  });
88
97
 
89
- return <div className={`search__results__cards ${activeTab ? `search__results__cards--${activeTab}` : ''}`}>{renderedResults}</div>;
98
+ return (
99
+ <div className={`search__results__cards ${activeTab ? `search__results__cards--${activeTab}` : ''}`}>
100
+ {selectedPackagingAccoResult && !isFlyIn && (
101
+ <HotelCard key={selectedPackagingAccoResult.code} result={selectedPackagingAccoResult} translations={translations} />
102
+ )}
103
+ {renderedResults}
104
+ </div>
105
+ );
90
106
  };
91
107
 
92
- const HotelAccommodationResults: React.FC<HotelAccommodationResultsProps> = ({ isLoading }) => {
108
+ const HotelAccommodationResults: React.FC<HotelAccommodationResultsProps> = ({ isLoading, isFlyIn }) => {
93
109
  const context = useContext(SearchResultsConfigurationContext);
94
-
110
+ const dispatch = useDispatch();
95
111
  if (!context) {
96
112
  return null;
97
113
  }
98
114
 
99
115
  const translations = getTranslations(context.languageCode ?? 'en-GB');
100
116
 
101
- const { filteredResults, filteredPackagingAccoResults, activeTab } = useSelector((state: SearchResultsRootState) => state.searchResults);
117
+ const { filteredResults, filteredPackagingAccoResults, packagingAccoResults, activeTab, flyInIsOpen, selectedPackagingAccoResultCode } = useSelector(
118
+ (state: SearchResultsRootState) => state.searchResults
119
+ );
102
120
 
103
121
  const cmsMap = React.useMemo(() => {
104
122
  const map = new Map();
@@ -147,26 +165,65 @@ const HotelAccommodationResults: React.FC<HotelAccommodationResultsProps> = ({ i
147
165
  const firstResultDay = firstResultDate ? format(parseISO(firstResultDate), 'd') : null;
148
166
  const firstResultMonth = firstResultDate ? format(parseISO(firstResultDate), 'MMM') : null;
149
167
 
168
+ const selectedPackagingAccoResult = React.useMemo(() => {
169
+ const selectedResult = packagingAccoResults.find((result) => result.code === selectedPackagingAccoResultCode);
170
+ if (selectedResult) {
171
+ return mapPackagingAccoResult(selectedResult, cmsMap.get(selectedResult.code), context.languageCode, translations);
172
+ }
173
+ }, [packagingAccoResults, selectedPackagingAccoResultCode, cmsMap, context.languageCode, translations]);
174
+
175
+ const visibleResults = React.useMemo(() => {
176
+ const shouldShowAll = context?.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight || isFlyIn;
177
+
178
+ if (shouldShowAll) {
179
+ return mappedResults;
180
+ }
181
+
182
+ if (selectedPackagingAccoResult) {
183
+ return mappedResults.filter((result) => result.code !== selectedPackagingAccoResult.code).slice(0, 2);
184
+ }
185
+
186
+ return mappedResults.slice(0, 3);
187
+ }, [context?.searchConfiguration.qsmType, mappedResults, isFlyIn, selectedPackagingAccoResult]);
188
+
189
+ const handleShowMoreHotels = (flyInType: FlyInType) => {
190
+ dispatch(setFlyInType(flyInType));
191
+ dispatch(setFlyInIsOpen(true));
192
+ };
193
+
150
194
  return (
151
195
  <>
152
- <div className="search__results__label search__results__label--secondary">
153
- <div className="search__results__label__date">
154
- {firstResultDay && firstResultMonth ? (
155
- <>
156
- <p className="search__results__label__date-date">{firstResultDay}</p>
157
- <p>{firstResultMonth}</p>
158
- </>
159
- ) : (
160
- <Icon name="ui-bed" height={16} fill="white" />
161
- )}
196
+ {!isFlyIn && (
197
+ <div className="search__results__label search__results__label--secondary">
198
+ <div className="search__results__label__date">
199
+ {firstResultDay && firstResultMonth ? (
200
+ <>
201
+ <p className="search__results__label__date-date">{firstResultDay}</p>
202
+ <p>{firstResultMonth}</p>
203
+ </>
204
+ ) : (
205
+ <Icon name="ui-bed" height={16} fill="white" />
206
+ )}
207
+ </div>
208
+ <div className="search__results__label__text">
209
+ <h3>
210
+ {translations.SRP.SELECT} <strong>{translations.SRP.ACCOMMODATION}</strong>
211
+ </h3>
212
+ </div>
162
213
  </div>
163
- <div className="search__results__label__text">
164
- <h3>
165
- {translations.SRP.SELECT} <strong>{translations.SRP.ACCOMMODATION}</strong>
166
- </h3>
214
+ )}
215
+ {isLoading ? (
216
+ <>{context.customSpinner ?? <Spinner />}</>
217
+ ) : (
218
+ renderHotelResults(visibleResults, context, activeTab, translations, selectedPackagingAccoResult, isFlyIn)
219
+ )}
220
+ {packagingAccoResults.length > 3 && !isFlyIn && (
221
+ <div className="search__results__cards__actions">
222
+ <button className="cta cta--secondary" onClick={() => handleShowMoreHotels('acco-results')}>
223
+ {translations.SRP.SHOW_MORE}
224
+ </button>
167
225
  </div>
168
- </div>
169
- {isLoading ? <>{context.customSpinner ?? <Spinner />}</> : renderHotelResults(mappedResults, context, activeTab, translations)}
226
+ )}
170
227
  {mappedResults.length === 0 && !isLoading && <div className="no-results">{translations.SRP.NO_RESULTS}</div>}
171
228
  </>
172
229
  );
@@ -16,15 +16,22 @@ import {
16
16
  setPackagingAccoSearchDetails,
17
17
  setEditablePackagingEntry,
18
18
  setTransactionId,
19
- setAccommodationFlyInStep,
19
+ setFlyInType,
20
20
  setPriceDetails,
21
21
  setItinerary,
22
22
  setFlightsLoading,
23
23
  setPackagingFlightResults,
24
24
  setSelectedPackagingFlight,
25
- setSelectedPackagingAccoResult
25
+ setSelectedPackagingAccoResult,
26
+ setSelectedOutwardKey,
27
+ setSelectedReturnKey,
28
+ setFilteredPackagingFlightResults,
29
+ setInitialFlightFilters,
30
+ resetFlightFilters,
31
+ setFilters,
32
+ setInitialFilters
26
33
  } from '../../store/search-results-slice';
27
- import { AccommodationFlyInStep, Filter, SearchSeed, SortByType } from '../../types';
34
+ import { FlyInType, Filter, SearchSeed, SortByType } from '../../types';
28
35
  import useMediaQuery from '../../../shared/utils/use-media-query-util';
29
36
  import ItemPicker from '../item-picker';
30
37
  import {
@@ -71,7 +78,9 @@ import GroupTourResults from '../group-tour/group-tour-results';
71
78
  import {
72
79
  applyFilters,
73
80
  applyFiltersToPackageAccoResults,
81
+ applyFiltersToPackageFlightResults,
74
82
  enrichFiltersWithPackageAccoResults,
83
+ enrichFiltersWithPackageFlightResults,
75
84
  enrichFiltersWithResults
76
85
  } from '../../utils/search-results-utils';
77
86
  import {
@@ -91,6 +100,15 @@ import { getFlightKey } from '../../utils/flight-utils';
91
100
  import IndependentFlightOption from '../flight/flight-selection/independent-flight-option';
92
101
  import { Spinner } from '../../..';
93
102
  import { PackagingRequestBase } from '@qite/tide-client/build/types/booking-v2/request/packaging/packaging-request-base';
103
+ import {
104
+ selectSelectedCombinationFlight,
105
+ selectSelectedOutward,
106
+ selectSelectedOutwardKey,
107
+ selectSelectedReturn,
108
+ selectSelectedReturnKey,
109
+ selectUniqueOutwardFlights,
110
+ selectUniqueReturnFlights
111
+ } from '../../store/search-results-selectors';
94
112
 
95
113
  type BuildPackagingEntryPartialArgs = {
96
114
  sourceEntry: PackagingEntry | null | undefined;
@@ -115,18 +133,20 @@ const SearchResultsContainer: React.FC = () => {
115
133
  filteredResults,
116
134
  packagingAccoResults,
117
135
  filteredPackagingAccoResults,
118
- bookingPackageDetails,
119
136
  isLoading,
120
137
  flightsLoading,
138
+ initialFilters,
121
139
  filters,
140
+ flightFilters,
122
141
  selectedSortType,
142
+ selectedFlightSortType,
123
143
  selectedSearchResult,
124
144
  selectedPackagingAccoResultCode,
125
145
  flyInIsOpen,
126
146
  packagingAccoSearchDetails,
127
147
  editablePackagingEntry,
128
148
  transactionId,
129
- accommodationFlyInStep,
149
+ flyInType,
130
150
  itinerary,
131
151
  packagingFlightResults
132
152
  } = useSelector((state: SearchResultsRootState) => state.searchResults);
@@ -134,7 +154,7 @@ const SearchResultsContainer: React.FC = () => {
134
154
  const isMobile = useMediaQuery('(max-width: 1200px)');
135
155
 
136
156
  const [initialFiltersSet, setInitialFiltersSet] = useState(false);
137
- const [initialFilters, setInitialFilters] = useState<Filter[]>([]);
157
+ const [initialFlightFiltersSet, setInitialFlightFiltersSet] = useState(false);
138
158
  const [filtersOpen, setFiltersOpen] = useState(false);
139
159
 
140
160
  const [detailsIsLoading, setDetailsIsLoading] = useState(false);
@@ -147,8 +167,6 @@ const SearchResultsContainer: React.FC = () => {
147
167
 
148
168
  const skipInitialPackagingAccoDetailsRef = useRef(false);
149
169
 
150
- const [showAllOutwardFlights, setShowAllOutwardFlights] = useState(false);
151
-
152
170
  const panelRef = useRef<HTMLDivElement | null>(null);
153
171
 
154
172
  const sortByTypes: SortByType[] = [
@@ -439,7 +457,7 @@ const SearchResultsContainer: React.FC = () => {
439
457
  setDetailsIsLoading(true);
440
458
 
441
459
  setSelectedAccommodationSeed(seed);
442
- dispatch(setAccommodationFlyInStep('results'));
460
+ dispatch(setFlyInType('acco-results'));
443
461
  handleFlyInToggle(true);
444
462
  const currentTransactionId = await getOrCreateTransactionId();
445
463
  await runAccommodationFlow(seed, currentTransactionId ?? '');
@@ -578,7 +596,7 @@ const SearchResultsContainer: React.FC = () => {
578
596
  const enrichedFilters = enrichFiltersWithResults(packageSearchResults, context.filters, context.tags ?? []);
579
597
  if (!initialFiltersSet) {
580
598
  dispatch(resetFilters(enrichedFilters));
581
- setInitialFilters(enrichedFilters);
599
+ dispatch(setInitialFilters(enrichedFilters));
582
600
  setInitialFiltersSet(true);
583
601
  }
584
602
 
@@ -637,7 +655,7 @@ const SearchResultsContainer: React.FC = () => {
637
655
 
638
656
  const packageAccoSearchResults = await searchPackagingAccommodations(config, searchRequest);
639
657
 
640
- const enrichedFilters = enrichFiltersWithPackageAccoResults(packageAccoSearchResults, context.filters, context.tags ?? []);
658
+ const enrichedFilters = enrichFiltersWithPackageAccoResults(packageAccoSearchResults, context.tags ?? []);
641
659
  if (!initialFiltersSet) {
642
660
  dispatch(resetFilters(enrichedFilters));
643
661
  setInitialFilters(enrichedFilters);
@@ -680,11 +698,24 @@ const SearchResultsContainer: React.FC = () => {
680
698
 
681
699
  const packageFlightSearchResults = await searchPackagingFlights(config, searchRequest);
682
700
 
701
+ const enrichedFilters = enrichFiltersWithPackageFlightResults(packageFlightSearchResults, context.tags ?? [], translations);
702
+ if (!initialFlightFiltersSet) {
703
+ dispatch(resetFlightFilters(enrichedFilters));
704
+ dispatch(setInitialFlightFilters(enrichedFilters));
705
+ setInitialFlightFiltersSet(true);
706
+ }
707
+
683
708
  dispatch(setPackagingFlightResults(packageFlightSearchResults));
684
- const firstResult = first(packageFlightSearchResults);
685
- if (firstResult) {
686
- setSelectedOutwardKey(getFlightKey(firstResult.outward.segments));
687
- setSelectedReturnKey(getFlightKey(firstResult.return.segments));
709
+
710
+ const initialFilteredResults = applyFiltersToPackageFlightResults(packageFlightSearchResults, filters, null);
711
+ dispatch(setFilteredPackagingFlightResults(initialFilteredResults));
712
+
713
+ if (initialFilteredResults.length > 0) {
714
+ const firstResult = first(packageFlightSearchResults);
715
+ if (firstResult) {
716
+ dispatch(setSelectedOutwardKey(getFlightKey(firstResult.outward.segments)));
717
+ dispatch(setSelectedReturnKey(getFlightKey(firstResult.return.segments)));
718
+ }
688
719
  }
689
720
 
690
721
  dispatch(setFlightsLoading(false));
@@ -839,6 +870,7 @@ const SearchResultsContainer: React.FC = () => {
839
870
  context?.searchConfiguration.qsmType === PortalQsmType.GroupTour
840
871
  ) {
841
872
  handleFlyInToggle(true);
873
+ dispatch(setFlyInType('acco-details'));
842
874
  }
843
875
 
844
876
  try {
@@ -923,11 +955,10 @@ const SearchResultsContainer: React.FC = () => {
923
955
  if (selectedPackagingAccoResultCode) {
924
956
  fetchPackagingAccoSearchDetails();
925
957
  }
926
- dispatch(setAccommodationFlyInStep('details'));
927
958
  }, [selectedSearchResult, selectedPackagingAccoResultCode]);
928
959
 
929
960
  useEffect(() => {
930
- if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation) {
961
+ if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation || context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
931
962
  const filteredPackageAccoResults = applyFiltersToPackageAccoResults(packagingAccoResults, filters, selectedSortType);
932
963
  dispatch(setFilteredPackagingAccoResults(filteredPackageAccoResults));
933
964
  } else {
@@ -936,6 +967,13 @@ const SearchResultsContainer: React.FC = () => {
936
967
  }
937
968
  }, [filters, results, packagingAccoResults, selectedSortType]);
938
969
 
970
+ useEffect(() => {
971
+ if (context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
972
+ const filteredPackageFlightResults = applyFiltersToPackageFlightResults(packagingFlightResults, flightFilters, selectedFlightSortType);
973
+ dispatch(setFilteredPackagingFlightResults(filteredPackageFlightResults));
974
+ }
975
+ }, [flightFilters, packagingFlightResults, selectedFlightSortType]);
976
+
939
977
  useEffect(() => {
940
978
  setInitialFiltersSet(false);
941
979
  }, [activeSearchSeed]);
@@ -985,7 +1023,6 @@ const SearchResultsContainer: React.FC = () => {
985
1023
  } as PackagingRequestBase<PackagingEntry>;
986
1024
 
987
1025
  const itinerary = await getItinerary(config, request);
988
- console.log('Fetched itinerary', itinerary);
989
1026
  dispatch(setItinerary(itinerary));
990
1027
  setItineraryIsLoading(false);
991
1028
  } catch (err) {
@@ -999,35 +1036,25 @@ const SearchResultsContainer: React.FC = () => {
999
1036
  }, [editablePackagingEntry]);
1000
1037
 
1001
1038
  // Flight selection
1002
- const [selectedOutwardKey, setSelectedOutwardKey] = useState<string | null>(null);
1003
- const [selectedReturnKey, setSelectedReturnKey] = useState<string | null>(null);
1004
-
1005
- const uniqueOutwardFlights: PackagingFlightResponse[] = React.useMemo(() => {
1006
- const map = new Map();
1007
-
1008
- packagingFlightResults.forEach((flight) => {
1009
- const key = getFlightKey(flight.outward.segments);
1010
-
1011
- if (!map.has(key)) {
1012
- map.set(key, flight);
1013
- }
1014
- });
1015
-
1016
- return Array.from(map.values());
1017
- }, [packagingFlightResults]);
1018
-
1019
- const [uniqueReturnFlights, setUniqueReturnFlights] = useState<PackagingFlightResponse[]>([]);
1039
+ // const [selectedOutwardKey, setSelectedOutwardKey] = useState<string | null>(null);
1040
+ // const [selectedReturnKey, setSelectedReturnKey] = useState<string | null>(null);
1041
+ const selectedOutwardKey = useSelector(selectSelectedOutwardKey);
1042
+ const selectedReturnKey = useSelector(selectSelectedReturnKey);
1043
+ const uniqueOutwardFlights = useSelector(selectUniqueOutwardFlights);
1044
+ const uniqueReturnFlights = useSelector(selectUniqueReturnFlights);
1045
+ const selectedOutward = useSelector(selectSelectedOutward);
1046
+ const selectedReturn = useSelector(selectSelectedReturn);
1047
+ const selectedCombinationFlight = useSelector(selectSelectedCombinationFlight);
1020
1048
 
1021
1049
  useEffect(() => {
1022
1050
  if (!selectedOutwardKey) {
1023
- setUniqueReturnFlights([]);
1024
- setSelectedReturnKey(null);
1051
+ dispatch(setSelectedReturnKey(null));
1025
1052
  return;
1026
1053
  }
1027
1054
 
1028
- // Filter combinations that match selected outward fare
1029
1055
  const matchingCombinations = packagingFlightResults.filter((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey);
1030
- const returnMap = new Map();
1056
+
1057
+ const returnMap = new Map<string, PackagingFlightResponse>();
1031
1058
 
1032
1059
  matchingCombinations.forEach((flight) => {
1033
1060
  const key = getFlightKey(flight.return.segments);
@@ -1038,34 +1065,18 @@ const SearchResultsContainer: React.FC = () => {
1038
1065
  });
1039
1066
 
1040
1067
  const returns = Array.from(returnMap.values());
1068
+ const segments = first(returns)?.return.segments;
1069
+ const firstReturnKey = returns.length > 0 && segments ? getFlightKey(segments) : null;
1041
1070
 
1042
- setUniqueReturnFlights(returns);
1043
- const firstReturnKey = returns.length > 0 ? getFlightKey(first(returns)?.return.segments) : null;
1044
-
1045
- if (firstReturnKey) {
1046
- setSelectedReturnKey(firstReturnKey);
1071
+ if (!returns.some((x) => getFlightKey(x.return.segments) === selectedReturnKey)) {
1072
+ dispatch(setSelectedReturnKey(firstReturnKey));
1047
1073
  }
1048
- }, [selectedOutwardKey, packagingFlightResults]);
1049
-
1050
- const selectedOutward = React.useMemo(() => {
1051
- if (!selectedOutwardKey) return null;
1052
-
1053
- return packagingFlightResults.find((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey) || null;
1054
- }, [packagingFlightResults, selectedOutwardKey]);
1055
-
1056
- const selectedReturn = React.useMemo(() => {
1057
- if (!selectedReturnKey) return null;
1074
+ }, [selectedOutwardKey, packagingFlightResults, selectedReturnKey, dispatch]);
1058
1075
 
1059
- return packagingFlightResults.find((flight) => getFlightKey(flight.return.segments) === selectedReturnKey) || null;
1060
- }, [packagingFlightResults, selectedReturnKey]);
1061
-
1062
- const selectedCombinationFlight = React.useMemo(() => {
1063
- if (!selectedOutwardKey || !selectedReturnKey) return undefined;
1064
-
1065
- return packagingFlightResults.find(
1066
- (flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey && getFlightKey(flight.return.segments) === selectedReturnKey
1067
- );
1068
- }, [packagingFlightResults, selectedOutwardKey, selectedReturnKey]);
1076
+ const visibleOutwardFlights = React.useMemo(() => {
1077
+ const withoutSelected = uniqueOutwardFlights.filter((x) => getFlightKey(x.outward.segments) !== selectedOutwardKey);
1078
+ return withoutSelected.slice(0, 3);
1079
+ }, [uniqueOutwardFlights, selectedOutwardKey]);
1069
1080
 
1070
1081
  // TODO: get details for selected combination flight and show in fly-in
1071
1082
  // useEffect(() => {
@@ -1076,6 +1087,7 @@ const SearchResultsContainer: React.FC = () => {
1076
1087
  // dispatch(setFlyInIsOpen(true));
1077
1088
  // }, [selectedCombinationFlight, dispatch]);
1078
1089
 
1090
+ // Build packagingEntry
1079
1091
  useEffect(() => {
1080
1092
  if (!context) return;
1081
1093
 
@@ -1367,9 +1379,10 @@ const SearchResultsContainer: React.FC = () => {
1367
1379
  } as PackagingEntry;
1368
1380
  };
1369
1381
 
1370
- const visibleOutwardFlights = React.useMemo(() => {
1371
- return showAllOutwardFlights ? uniqueOutwardFlights : uniqueOutwardFlights.slice(0, 3);
1372
- }, [showAllOutwardFlights, uniqueOutwardFlights]);
1382
+ const handleShowMoreFlights = (flyInType: FlyInType) => {
1383
+ dispatch(setFlyInType(flyInType));
1384
+ dispatch(setFlyInIsOpen(true));
1385
+ };
1373
1386
 
1374
1387
  return (
1375
1388
  <div id="tide-booking" className="search__bg">
@@ -1380,7 +1393,6 @@ const SearchResultsContainer: React.FC = () => {
1380
1393
  <FlightSearchProvider tideConnection={context.tideConnection}>
1381
1394
  <FlightResultsContainer isMobile={isMobile} />
1382
1395
  <FlyIn
1383
- title="Select your fare"
1384
1396
  srpType={context.searchConfiguration.qsmType}
1385
1397
  isOpen={flyInIsOpen}
1386
1398
  setIsOpen={handleFlyInToggle}
@@ -1402,6 +1414,8 @@ const SearchResultsContainer: React.FC = () => {
1402
1414
  handleSetIsOpen={() => setFiltersOpen(!filtersOpen)}
1403
1415
  // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
1404
1416
  isLoading={isLoading}
1417
+ setFilters={(filters) => dispatch(setFilters(filters))}
1418
+ resetFilters={(filters) => dispatch(resetFilters(filters))}
1405
1419
  />
1406
1420
  )}
1407
1421
  {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
@@ -1444,33 +1458,35 @@ const SearchResultsContainer: React.FC = () => {
1444
1458
  )}
1445
1459
  </div>
1446
1460
  )}
1447
- <div className="search__result-row">
1448
- <span className="search__result-row-text">
1449
- {!isLoading && context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && (
1450
- <>
1451
- {context.searchConfiguration.qsmType === PortalQsmType.Accommodation &&
1452
- filteredPackagingAccoResults?.length &&
1453
- filteredPackagingAccoResults?.length}
1454
- {context.searchConfiguration.qsmType !== PortalQsmType.Accommodation && filteredResults?.length && filteredResults.length}
1455
- &nbsp;{translations.SRP.TOTAL_RESULTS_LABEL}
1456
- </>
1461
+ {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && (
1462
+ <div className="search__result-row">
1463
+ <span className="search__result-row-text">
1464
+ {!isLoading && (
1465
+ <>
1466
+ {context.searchConfiguration.qsmType === PortalQsmType.Accommodation &&
1467
+ filteredPackagingAccoResults?.length &&
1468
+ filteredPackagingAccoResults?.length}
1469
+ {context.searchConfiguration.qsmType !== PortalQsmType.Accommodation && filteredResults?.length && filteredResults.length}
1470
+ &nbsp;{translations.SRP.TOTAL_RESULTS_LABEL}
1471
+ </>
1472
+ )}
1473
+ </span>
1474
+ {!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
1475
+ <div className="search__result-row-filter">
1476
+ <ItemPicker
1477
+ items={sortByTypes}
1478
+ selection={selectedSortType?.label || undefined}
1479
+ selectedSortByType={selectedSortType}
1480
+ label={translations.SRP.SORTBY}
1481
+ placeholder={translations.SRP.SORTBY}
1482
+ classModifier="travel-class-picker__items"
1483
+ valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1484
+ onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1485
+ />
1486
+ </div>
1457
1487
  )}
1458
- </span>
1459
- {!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
1460
- <div className="search__result-row-filter">
1461
- <ItemPicker
1462
- items={sortByTypes}
1463
- selection={selectedSortType?.label || undefined}
1464
- selectedSortByType={selectedSortType}
1465
- label={translations.SRP.SORTBY}
1466
- placeholder={translations.SRP.SORTBY}
1467
- classModifier="travel-class-picker__items"
1468
- valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1469
- onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1470
- />
1471
- </div>
1472
- )}
1473
- </div>
1488
+ </div>
1489
+ )}
1474
1490
 
1475
1491
  <div className="search__results__wrapper">
1476
1492
  {context.showTabViews &&
@@ -1506,7 +1522,7 @@ const SearchResultsContainer: React.FC = () => {
1506
1522
  key={`flight-${selectedOutwardKey}`}
1507
1523
  item={selectedOutward.outward}
1508
1524
  guid={selectedOutward.outwardGuid}
1509
- onSelect={() => setSelectedOutwardKey(null)}
1525
+ onSelect={() => dispatch(setSelectedOutwardKey(null))}
1510
1526
  selectedGuid={selectedOutward.outwardGuid}
1511
1527
  isOutward={true}
1512
1528
  showSelectedState={true}
@@ -1517,7 +1533,7 @@ const SearchResultsContainer: React.FC = () => {
1517
1533
  <IndependentFlightOption
1518
1534
  key={`flight-${result.outwardGuid}`}
1519
1535
  item={result.outward}
1520
- onSelect={() => setSelectedOutwardKey(getFlightKey(result.outward.segments))}
1536
+ onSelect={() => dispatch(setSelectedOutwardKey(getFlightKey(result.outward.segments)))}
1521
1537
  guid={result.outwardGuid}
1522
1538
  isOutward={true}
1523
1539
  price={result.price}
@@ -1527,8 +1543,8 @@ const SearchResultsContainer: React.FC = () => {
1527
1543
  </div>
1528
1544
  {uniqueOutwardFlights && uniqueOutwardFlights.length > 3 && (
1529
1545
  <div className="search__results__cards__actions">
1530
- <button className="cta cta--secondary" onClick={() => setShowAllOutwardFlights(!showAllOutwardFlights)}>
1531
- {showAllOutwardFlights ? translations.SRP.SHOW_LESS : translations.SRP.SHOW_MORE}
1546
+ <button className="cta cta--secondary" onClick={() => handleShowMoreFlights('flight-outward-results')}>
1547
+ {translations.SRP.SHOW_MORE}
1532
1548
  </button>
1533
1549
  </div>
1534
1550
  )}
@@ -1570,7 +1586,7 @@ const SearchResultsContainer: React.FC = () => {
1570
1586
  <IndependentFlightOption
1571
1587
  key={`flight-${result.outwardGuid}`}
1572
1588
  item={result.return}
1573
- onSelect={() => setSelectedReturnKey(getFlightKey(result.return.segments))}
1589
+ onSelect={() => dispatch(setSelectedReturnKey(getFlightKey(result.return.segments)))}
1574
1590
  guid={result.outwardGuid}
1575
1591
  isOutward={false}
1576
1592
  currentSelectedPrice={selectedReturn?.price}
@@ -1588,15 +1604,15 @@ const SearchResultsContainer: React.FC = () => {
1588
1604
  </div>
1589
1605
  {/* <button onClick={() => handleFlyInToggle(!flyInIsOpen)}>Toggle FlyIn</button> */}
1590
1606
  <FlyIn
1591
- title={`${translations.SRP.SELECT} ${translations.SRP.ACCOMMODATION}`}
1592
1607
  srpType={context.searchConfiguration.qsmType}
1593
1608
  isOpen={flyInIsOpen}
1594
1609
  setIsOpen={handleFlyInToggle}
1595
1610
  handleConfirm={() => handleConfirmHotelSwap()}
1596
1611
  onPanelRef={(el) => (panelRef.current = el)}
1597
1612
  detailsLoading={detailsIsLoading}
1598
- accommodationStep={accommodationFlyInStep}
1613
+ flyInType={flyInType}
1599
1614
  isPackageEditFlow={!!context.packagingEntry}
1615
+ sortByTypes={sortByTypes}
1600
1616
  />
1601
1617
  </>
1602
1618
  )}