@qite/tide-booking-component 1.4.33 → 1.4.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/build-cjs/index.js +48 -43
- package/build/build-esm/index.js +48 -43
- package/package.json +77 -77
- package/src/booking-product/components/product.tsx +26 -22
- package/src/booking-wizard/features/booking/booking-self-contained.tsx +304 -304
- package/src/qsm/components/QSMContainer/qsm-container.tsx +215 -218
- package/src/qsm/store/qsm-slice.ts +261 -261
- package/src/qsm/types.ts +145 -145
- package/src/search-results/components/search-results-container/search-results-container.tsx +341 -337
|
@@ -1,337 +1,341 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useState } from 'react';
|
|
2
|
-
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
-
import { SearchResultsRootState } from '../../store/search-results-store';
|
|
4
|
-
import SearchResultsConfigurationContext from '../../search-results-configuration-context';
|
|
5
|
-
|
|
6
|
-
import { resetFilters, setSortKey, setResults, setIsLoading, setSelectedHotel } from '../../store/search-results-slice';
|
|
7
|
-
import { Filter, SortingOption } from '../../types';
|
|
8
|
-
import useMediaQuery from '../../../shared/utils/use-media-query-util';
|
|
9
|
-
import Filters from '../filters/filters';
|
|
10
|
-
import ItemPicker from '../item-picker';
|
|
11
|
-
|
|
12
|
-
import { TideClientConfig, details, search } from '@qite/tide-client';
|
|
13
|
-
import {
|
|
14
|
-
BookingPackageDestination,
|
|
15
|
-
BookingPackageDetailsRequest,
|
|
16
|
-
BookingPackagePax,
|
|
17
|
-
BookingPackageRequest,
|
|
18
|
-
BookingPackageRequestRoom,
|
|
19
|
-
BookingPackageSearchRequest
|
|
20
|
-
} from '@qite/tide-client/build/types';
|
|
21
|
-
import { getDateFromParams, getNumberFromParams, getRoomsFromParams } from '../../../shared/utils/query-string-util';
|
|
22
|
-
import { range } from 'lodash';
|
|
23
|
-
import { Room } from '../../../booking-wizard/types';
|
|
24
|
-
import Icon from '../icon';
|
|
25
|
-
import Itinerary from '../itinerary';
|
|
26
|
-
import TabViews from '../tab-views';
|
|
27
|
-
import HotelAccommodationResults from '../hotel/hotel-accommodation-results';
|
|
28
|
-
import RoundTripResults from '../round-trip/round-trip-results';
|
|
29
|
-
import { enrichFiltersWithResults } from '../filters/utility';
|
|
30
|
-
|
|
31
|
-
const SearchResultsContainer: React.FC = () => {
|
|
32
|
-
const isMobile = useMediaQuery('(max-width: 1200px)');
|
|
33
|
-
const dispatch = useDispatch();
|
|
34
|
-
const context = useContext(SearchResultsConfigurationContext);
|
|
35
|
-
const { results, isLoading, filters, sortKey, selectedHotelId } = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
36
|
-
|
|
37
|
-
const [searchTrigger, setSearchTrigger] = useState(0);
|
|
38
|
-
const [initialFiltersSet, setInitialFiltersSet] = useState(false);
|
|
39
|
-
const [initialFilters, setInitialFilters] = useState<Filter[]>([]);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const runSearch = async () => {
|
|
43
|
-
dispatch(setIsLoading(true));
|
|
44
|
-
try {
|
|
45
|
-
if (!context) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const params = new URLSearchParams(location.search);
|
|
50
|
-
let from = getDateFromParams(params, 'fromDate');
|
|
51
|
-
let to = getDateFromParams(params, 'toDate');
|
|
52
|
-
const rooms = getRoomsFromParams(params, 'rooms');
|
|
53
|
-
let country = getNumberFromParams(params, 'country');
|
|
54
|
-
let region = getNumberFromParams(params, 'region');
|
|
55
|
-
let oord = getNumberFromParams(params, 'oord');
|
|
56
|
-
let city = getNumberFromParams(params, 'location');
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let
|
|
74
|
-
let
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
dispatch(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
]
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
{
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
{
|
|
323
|
-
|
|
324
|
-
{context.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
1
|
+
import React, { useContext, useEffect, useState } from 'react';
|
|
2
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
|
+
import { SearchResultsRootState } from '../../store/search-results-store';
|
|
4
|
+
import SearchResultsConfigurationContext from '../../search-results-configuration-context';
|
|
5
|
+
|
|
6
|
+
import { resetFilters, setSortKey, setResults, setIsLoading, setSelectedHotel } from '../../store/search-results-slice';
|
|
7
|
+
import { Filter, SortingOption } from '../../types';
|
|
8
|
+
import useMediaQuery from '../../../shared/utils/use-media-query-util';
|
|
9
|
+
import Filters from '../filters/filters';
|
|
10
|
+
import ItemPicker from '../item-picker';
|
|
11
|
+
|
|
12
|
+
import { TideClientConfig, details, search } from '@qite/tide-client';
|
|
13
|
+
import {
|
|
14
|
+
BookingPackageDestination,
|
|
15
|
+
BookingPackageDetailsRequest,
|
|
16
|
+
BookingPackagePax,
|
|
17
|
+
BookingPackageRequest,
|
|
18
|
+
BookingPackageRequestRoom,
|
|
19
|
+
BookingPackageSearchRequest
|
|
20
|
+
} from '@qite/tide-client/build/types';
|
|
21
|
+
import { getDateFromParams, getNumberFromParams, getRoomsFromParams } from '../../../shared/utils/query-string-util';
|
|
22
|
+
import { range } from 'lodash';
|
|
23
|
+
import { Room } from '../../../booking-wizard/types';
|
|
24
|
+
import Icon from '../icon';
|
|
25
|
+
import Itinerary from '../itinerary';
|
|
26
|
+
import TabViews from '../tab-views';
|
|
27
|
+
import HotelAccommodationResults from '../hotel/hotel-accommodation-results';
|
|
28
|
+
import RoundTripResults from '../round-trip/round-trip-results';
|
|
29
|
+
import { enrichFiltersWithResults } from '../filters/utility';
|
|
30
|
+
|
|
31
|
+
const SearchResultsContainer: React.FC = () => {
|
|
32
|
+
const isMobile = useMediaQuery('(max-width: 1200px)');
|
|
33
|
+
const dispatch = useDispatch();
|
|
34
|
+
const context = useContext(SearchResultsConfigurationContext);
|
|
35
|
+
const { results, isLoading, filters, sortKey, selectedHotelId } = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
36
|
+
|
|
37
|
+
const [searchTrigger, setSearchTrigger] = useState(0);
|
|
38
|
+
const [initialFiltersSet, setInitialFiltersSet] = useState(false);
|
|
39
|
+
const [initialFilters, setInitialFilters] = useState<Filter[]>([]);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const runSearch = async () => {
|
|
43
|
+
dispatch(setIsLoading(true));
|
|
44
|
+
try {
|
|
45
|
+
if (!context) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const params = new URLSearchParams(location.search);
|
|
50
|
+
let from = getDateFromParams(params, 'fromDate');
|
|
51
|
+
let to = getDateFromParams(params, 'toDate');
|
|
52
|
+
const rooms = getRoomsFromParams(params, 'rooms');
|
|
53
|
+
let country = getNumberFromParams(params, 'country');
|
|
54
|
+
let region = getNumberFromParams(params, 'region');
|
|
55
|
+
let oord = getNumberFromParams(params, 'oord');
|
|
56
|
+
let city = getNumberFromParams(params, 'location');
|
|
57
|
+
let hotel = getNumberFromParams(params, 'hotel');
|
|
58
|
+
let tagId = getNumberFromParams(params, 'tagId');
|
|
59
|
+
|
|
60
|
+
// temp hardcoded params
|
|
61
|
+
if (!from || !to) {
|
|
62
|
+
from = '2026-04-07';
|
|
63
|
+
to = '2026-04-13';
|
|
64
|
+
}
|
|
65
|
+
if (!country && !region && !oord && !city) {
|
|
66
|
+
region = 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof window !== 'undefined') {
|
|
70
|
+
window.scrollTo(0, 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let destinationId: number | null = null;
|
|
74
|
+
let destinationIsCountry = false;
|
|
75
|
+
let destinationIsRegion = false;
|
|
76
|
+
let destinationIsOord = false;
|
|
77
|
+
let destinationIsLocation = false;
|
|
78
|
+
|
|
79
|
+
if (country) {
|
|
80
|
+
destinationId = country;
|
|
81
|
+
destinationIsCountry = true;
|
|
82
|
+
} else if (region) {
|
|
83
|
+
destinationId = region;
|
|
84
|
+
destinationIsRegion = true;
|
|
85
|
+
} else if (oord) {
|
|
86
|
+
destinationId = oord;
|
|
87
|
+
destinationIsOord = true;
|
|
88
|
+
} else if (city) {
|
|
89
|
+
destinationId = city;
|
|
90
|
+
destinationIsLocation = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const searchRequest: BookingPackageRequest<BookingPackageSearchRequest> = {
|
|
94
|
+
officeId: 1,
|
|
95
|
+
payload: {
|
|
96
|
+
catalogueIds: context.tideConnection.catalogueIds ?? [],
|
|
97
|
+
serviceType:
|
|
98
|
+
context?.type === 'hotel' || context?.type === 'hotel-flight' ? 3 : context?.type === 'flight' ? 7 : context?.type === 'roundTrip' ? 1 : 0,
|
|
99
|
+
searchType: 0,
|
|
100
|
+
destination: {
|
|
101
|
+
id: Number(destinationId),
|
|
102
|
+
isCountry: destinationIsCountry,
|
|
103
|
+
isRegion: destinationIsRegion,
|
|
104
|
+
isOord: destinationIsOord,
|
|
105
|
+
isLocation: destinationIsLocation
|
|
106
|
+
} as BookingPackageDestination,
|
|
107
|
+
rooms: getRequestRooms(rooms),
|
|
108
|
+
fromDate: from,
|
|
109
|
+
toDate: to,
|
|
110
|
+
earliestFromOffset: 0,
|
|
111
|
+
latestToOffset: 0,
|
|
112
|
+
includeFlights: true,
|
|
113
|
+
regimeCodes:
|
|
114
|
+
filters
|
|
115
|
+
.find((f) => f.property === 'regime')
|
|
116
|
+
?.options?.filter((o) => o.isChecked)
|
|
117
|
+
.flatMap((o) => o.value.toString()) || [],
|
|
118
|
+
minPrice: filters.find((f) => f.property === 'price')?.selectedMin,
|
|
119
|
+
maxPrice: filters.find((f) => f.property === 'price')?.selectedMax,
|
|
120
|
+
useExactDates: true,
|
|
121
|
+
onlyCachedResults: false,
|
|
122
|
+
includeAllAllotments: true,
|
|
123
|
+
productIds: hotel ? [hotel] : [],
|
|
124
|
+
productTagIds: tagId ? [tagId] : []
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const config: TideClientConfig = {
|
|
129
|
+
host: context.tideConnection.host,
|
|
130
|
+
apiKey: context.tideConnection.apiKey
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const packageSearchResults = await search(config, searchRequest);
|
|
134
|
+
|
|
135
|
+
console.log('Search results', packageSearchResults);
|
|
136
|
+
|
|
137
|
+
const enrichedFilters = enrichFiltersWithResults(packageSearchResults, context.filters);
|
|
138
|
+
if (!initialFiltersSet) {
|
|
139
|
+
dispatch(resetFilters(enrichedFilters));
|
|
140
|
+
setInitialFilters(enrichedFilters);
|
|
141
|
+
setInitialFiltersSet(true);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
dispatch(setResults({ results: packageSearchResults }));
|
|
145
|
+
if (packageSearchResults?.length > 0) {
|
|
146
|
+
dispatch(setSelectedHotel(packageSearchResults[0].productId));
|
|
147
|
+
}
|
|
148
|
+
dispatch(setIsLoading(false));
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error('Search failed', err);
|
|
151
|
+
dispatch(setIsLoading(false));
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
runSearch();
|
|
156
|
+
}, [location.search, searchTrigger]);
|
|
157
|
+
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
const fetchPackageDetails = async () => {
|
|
160
|
+
if (!selectedHotelId || !context) return;
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const config: TideClientConfig = {
|
|
164
|
+
host: context.tideConnection.host,
|
|
165
|
+
apiKey: context.tideConnection.apiKey
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const selectedItem = results.find((r) => r.productId === selectedHotelId);
|
|
169
|
+
if (!selectedItem) return;
|
|
170
|
+
|
|
171
|
+
const params = new URLSearchParams(location.search);
|
|
172
|
+
const rooms = getRoomsFromParams(params, 'rooms');
|
|
173
|
+
const requestRooms = getRequestRooms(rooms);
|
|
174
|
+
|
|
175
|
+
const detailsRequest: BookingPackageRequest<BookingPackageDetailsRequest> = {
|
|
176
|
+
officeId: 1,
|
|
177
|
+
payload: {
|
|
178
|
+
catalogueId: selectedItem.catalogueId,
|
|
179
|
+
rooms: requestRooms,
|
|
180
|
+
searchType: 0, // same as search
|
|
181
|
+
productCode: selectedItem.code,
|
|
182
|
+
fromDate: selectedItem.fromDate,
|
|
183
|
+
toDate: selectedItem.toDate,
|
|
184
|
+
includeFlights: true,
|
|
185
|
+
includeHotels: true,
|
|
186
|
+
includePaxTypes: true,
|
|
187
|
+
checkExternalAvailability: true,
|
|
188
|
+
expectedPrice: selectedItem.price,
|
|
189
|
+
duration: null,
|
|
190
|
+
preNights: null,
|
|
191
|
+
postNights: null
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const detailsResponse = await details(config, detailsRequest);
|
|
196
|
+
|
|
197
|
+
console.log('Package details:', detailsResponse);
|
|
198
|
+
|
|
199
|
+
// TODO: store flights / details in redux
|
|
200
|
+
// dispatch(setSelectedHotelDetails(details));
|
|
201
|
+
} catch (err) {
|
|
202
|
+
console.error('Failed to fetch package details', err);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
fetchPackageDetails();
|
|
207
|
+
}, [selectedHotelId]);
|
|
208
|
+
|
|
209
|
+
const getRequestRooms = (rooms: Room[] | null) => {
|
|
210
|
+
if (!rooms) {
|
|
211
|
+
// Fall back to 2 adults
|
|
212
|
+
var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
|
|
213
|
+
range(0, 2).forEach(() => {
|
|
214
|
+
room.pax.push({
|
|
215
|
+
age: 30
|
|
216
|
+
} as BookingPackagePax);
|
|
217
|
+
});
|
|
218
|
+
return [room];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const requestRooms = rooms?.map((x, i) => {
|
|
222
|
+
var room = { index: i, pax: [] } as BookingPackageRequestRoom;
|
|
223
|
+
range(0, x.adults).forEach(() => {
|
|
224
|
+
room.pax.push({
|
|
225
|
+
age: 30
|
|
226
|
+
} as BookingPackagePax);
|
|
227
|
+
});
|
|
228
|
+
x.childAges.forEach((x) => {
|
|
229
|
+
room.pax.push({
|
|
230
|
+
age: x
|
|
231
|
+
} as BookingPackagePax);
|
|
232
|
+
});
|
|
233
|
+
return room;
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return requestRooms;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const [isMobileFiltersOpen, setIsMobileFiltersOpen] = useState(false);
|
|
240
|
+
|
|
241
|
+
const handleSortChange = (newSortKey: string) => {
|
|
242
|
+
dispatch(setSortKey(newSortKey));
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const handleSetIsMobileFiltersOpen = () => {
|
|
246
|
+
setIsMobileFiltersOpen(!isMobileFiltersOpen);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
if (typeof document !== 'undefined') {
|
|
251
|
+
document.body.classList.toggle('has-overlay', isMobileFiltersOpen);
|
|
252
|
+
}
|
|
253
|
+
}, [isMobileFiltersOpen]);
|
|
254
|
+
|
|
255
|
+
const sortingOptions: SortingOption[] = [
|
|
256
|
+
{ key: 'price-asc', label: 'Price: Low to High' },
|
|
257
|
+
{ key: 'price-desc', label: 'Price: High to Low' },
|
|
258
|
+
{ key: 'departure-date', label: 'Departure Date' }
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<div id="tide-booking" className="search__bg">
|
|
263
|
+
{context && (
|
|
264
|
+
<div className="search">
|
|
265
|
+
<div className="search__container">
|
|
266
|
+
{context.showFilters && (
|
|
267
|
+
<Filters
|
|
268
|
+
filters={filters}
|
|
269
|
+
isMobileFiltersOpen={isMobileFiltersOpen}
|
|
270
|
+
handleSetIsMobileFiltersOpen={handleSetIsMobileFiltersOpen}
|
|
271
|
+
handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
|
|
272
|
+
isLoading={isLoading}
|
|
273
|
+
/>
|
|
274
|
+
)}
|
|
275
|
+
{context.type === 'hotel-flight' && (
|
|
276
|
+
<Itinerary isMobileFiltersOpen={isMobileFiltersOpen} handleSetIsMobileFiltersOpen={handleSetIsMobileFiltersOpen} isLoading={isLoading} />
|
|
277
|
+
)}
|
|
278
|
+
{/* ---------------- Results ---------------- */}
|
|
279
|
+
<div className="search__results">
|
|
280
|
+
{isMobile && (
|
|
281
|
+
<div className="search__result-row">
|
|
282
|
+
<div className="cta cta--filter" onClick={() => setIsMobileFiltersOpen(true)}>
|
|
283
|
+
<Icon name="ui-filter" className="mobile-filters-button__icon" height={16} />
|
|
284
|
+
{context.translations?.filters}
|
|
285
|
+
</div>
|
|
286
|
+
{sortingOptions && sortingOptions.length > 0 && (
|
|
287
|
+
<ItemPicker
|
|
288
|
+
items={sortingOptions}
|
|
289
|
+
selection={sortKey || undefined}
|
|
290
|
+
label="Sort by"
|
|
291
|
+
placeholder="Sort by"
|
|
292
|
+
classModifier="travel-class-picker__items"
|
|
293
|
+
onPick={handleSortChange}
|
|
294
|
+
/>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
)}
|
|
298
|
+
|
|
299
|
+
<div className="search__result-row">
|
|
300
|
+
<span className="search__result-row-text">
|
|
301
|
+
{!isLoading && (
|
|
302
|
+
<>
|
|
303
|
+
{results?.length ?? 4} {context.translations?.totalResultsLabel}
|
|
304
|
+
</>
|
|
305
|
+
)}
|
|
306
|
+
</span>
|
|
307
|
+
{!isMobile && sortingOptions && sortingOptions.length > 0 && (
|
|
308
|
+
<div className="search__result-row-filter">
|
|
309
|
+
<ItemPicker
|
|
310
|
+
items={sortingOptions}
|
|
311
|
+
selection={sortKey || undefined}
|
|
312
|
+
label="Sort by"
|
|
313
|
+
placeholder="Sort by"
|
|
314
|
+
classModifier="travel-class-picker__items"
|
|
315
|
+
onPick={handleSortChange}
|
|
316
|
+
/>
|
|
317
|
+
</div>
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div className="search__results__wrapper">
|
|
322
|
+
{context.showTabViews && <TabViews />}
|
|
323
|
+
|
|
324
|
+
{context.showRoundTripResults && <RoundTripResults />}
|
|
325
|
+
|
|
326
|
+
{/* {context.showFlightResults && <FlightResults isDeparture={true} />} */}
|
|
327
|
+
|
|
328
|
+
{context.showHotelAccommodationResults && <HotelAccommodationResults isLoading={isLoading} context={context} />}
|
|
329
|
+
{/* {context.showFlightAccommodationResults && <FlightAccommodationResults />} */}
|
|
330
|
+
|
|
331
|
+
{/* {context.showFlightResults && <FlightResults isDeparture={false} />} */}
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
)}
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
export default SearchResultsContainer;
|