@qite/tide-booking-component 1.4.35 → 1.4.37
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 +912 -192
- package/build/build-cjs/qsm/types.d.ts +1 -0
- package/build/build-cjs/search-results/components/flight/flight-results.d.ts +1 -1
- package/build/build-cjs/search-results/types.d.ts +5 -0
- package/build/build-esm/index.js +912 -192
- package/build/build-esm/qsm/types.d.ts +1 -0
- package/build/build-esm/search-results/components/flight/flight-results.d.ts +1 -1
- package/build/build-esm/search-results/types.d.ts +5 -0
- package/package.json +2 -2
- package/src/qsm/components/mobile-filter-modal/index.tsx +67 -42
- package/src/qsm/components/search-input-group/index.tsx +16 -2
- package/src/qsm/types.ts +1 -0
- package/src/search-results/components/flight/flight-results.tsx +115 -113
- package/src/search-results/components/hotel/hotel-accommodation-results.tsx +85 -0
- package/src/search-results/components/itinerary/index.tsx +11 -9
- package/src/search-results/components/search-results-container/search-results-container.tsx +294 -146
- package/src/search-results/features/flights/flight-search-results-self-contained.tsx +5 -1
- package/src/search-results/features/hotels/hotel-flight-search-results-self-contained.tsx +5 -1
- package/src/search-results/features/hotels/hotel-search-results-self-contained.tsx +5 -1
- package/src/search-results/features/roundtrips/roundtrip-search-results-self-contained.tsx +5 -1
- package/src/search-results/types.ts +3 -1
|
@@ -114,15 +114,17 @@ const Itinerary: React.FC<ItineraryProps> = ({ isOpen, handleSetIsOpen, isLoadin
|
|
|
114
114
|
) : (
|
|
115
115
|
<>
|
|
116
116
|
<div className="search__filter-group">
|
|
117
|
-
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
117
|
+
{context?.destinationImage && (
|
|
118
|
+
<div className="search__filter__image__wrapper">
|
|
119
|
+
<img src={context.destinationImage.url} alt={context.destinationImage.alt} className="search__filter__image" />
|
|
120
|
+
{entry?.number && (
|
|
121
|
+
<span className="search__filter__image__text">
|
|
122
|
+
{translations.SRP.DOSSIER_NUMBER}: {entry?.number}
|
|
123
|
+
</span>
|
|
124
|
+
)}
|
|
125
|
+
<h4 className="search__filter__image__title">{(location || '') + (location && country ? ' - ' : '') + (country || '')}</h4>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
126
128
|
</div>
|
|
127
129
|
|
|
128
130
|
<div className="search__filter__prices">
|
|
@@ -9,16 +9,18 @@ import useMediaQuery from '../../../shared/utils/use-media-query-util';
|
|
|
9
9
|
import Filters from '../filters/filters';
|
|
10
10
|
import ItemPicker from '../item-picker';
|
|
11
11
|
|
|
12
|
-
import { TideClientConfig, detailsWL, search } from '@qite/tide-client';
|
|
12
|
+
import { TideClientConfig, details, detailsWL, getEntryLight, search } from '@qite/tide-client';
|
|
13
13
|
import {
|
|
14
14
|
BookingPackageDestination,
|
|
15
15
|
BookingPackageDetailsRequest,
|
|
16
16
|
BookingPackagePax,
|
|
17
17
|
BookingPackageRequest,
|
|
18
18
|
BookingPackageRequestRoom,
|
|
19
|
-
BookingPackageSearchRequest
|
|
19
|
+
BookingPackageSearchRequest,
|
|
20
|
+
EntryLight,
|
|
21
|
+
EntryRoom
|
|
20
22
|
} from '@qite/tide-client/build/types';
|
|
21
|
-
import { getDateFromParams, getNumberFromParams, getRoomsFromParams } from '../../../shared/utils/query-string-util';
|
|
23
|
+
import { getDateFromParams, getNumberFromParams, getRoomsFromParams, getStringFromParams } from '../../../shared/utils/query-string-util';
|
|
22
24
|
import { range } from 'lodash';
|
|
23
25
|
import { Room } from '../../../booking-wizard/types';
|
|
24
26
|
import Icon from '../icon';
|
|
@@ -29,17 +31,247 @@ import RoundTripResults from '../round-trip/round-trip-results';
|
|
|
29
31
|
import { enrichFiltersWithResults } from '../filters/utility';
|
|
30
32
|
import FlightResults from '../flight/flight-results';
|
|
31
33
|
import { getTranslations } from '../../../shared/utils/localization-util';
|
|
34
|
+
import filters from '../filters/filters';
|
|
35
|
+
import FlightAccommodationResults from '../flight/flight-accommodation-results';
|
|
32
36
|
|
|
33
37
|
const SearchResultsContainer: React.FC = () => {
|
|
34
|
-
const isMobile = useMediaQuery('(max-width: 1200px)');
|
|
35
38
|
const dispatch = useDispatch();
|
|
39
|
+
|
|
36
40
|
const context = useContext(SearchResultsConfigurationContext);
|
|
37
41
|
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
38
|
-
|
|
42
|
+
|
|
43
|
+
const { results, bookingPackageDetails, entry, isLoading, filters, sortKey, selectedHotelId } = useSelector(
|
|
44
|
+
(state: SearchResultsRootState) => state.searchResults
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const isMobile = useMediaQuery('(max-width: 1200px)');
|
|
39
48
|
|
|
40
49
|
const [searchTrigger, setSearchTrigger] = useState(0);
|
|
41
50
|
const [initialFiltersSet, setInitialFiltersSet] = useState(false);
|
|
42
51
|
const [initialFilters, setInitialFilters] = useState<Filter[]>([]);
|
|
52
|
+
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
53
|
+
const [itineraryOpen, setItineraryOpen] = useState(false);
|
|
54
|
+
|
|
55
|
+
const sortingOptions: SortingOption[] = [
|
|
56
|
+
{ key: 'price-asc', label: translations.SRP.PRICE_ASC },
|
|
57
|
+
{ key: 'price-desc', label: translations.SRP.PRICE_DESC },
|
|
58
|
+
{ key: 'departure-date', label: translations.SRP.DEPARTURE_ASC }
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const handleSortChange = (newSortKey: string) => {
|
|
62
|
+
dispatch(setSortKey(newSortKey));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const buildSearchFromEntry = (entry: EntryLight): BookingPackageRequest<BookingPackageSearchRequest> => {
|
|
66
|
+
const from = new Date(Math.min(...entry.items.map((i) => i.startDate.getTime()))).toISOString();
|
|
67
|
+
const to = new Date(Math.max(...entry.items.map((i) => i.endDate.getTime()))).toISOString();
|
|
68
|
+
const rooms = entry.rooms;
|
|
69
|
+
|
|
70
|
+
const hotelItem = entry.items.find((i) => i.productType === 3);
|
|
71
|
+
|
|
72
|
+
let country = hotelItem ? hotelItem.countryId : null;
|
|
73
|
+
let region = hotelItem ? hotelItem.regionId : null;
|
|
74
|
+
let oord = hotelItem ? hotelItem.oordId : null;
|
|
75
|
+
let city = hotelItem ? hotelItem.locationId : null;
|
|
76
|
+
let hotel = hotelItem ? hotelItem.productCode : null;
|
|
77
|
+
|
|
78
|
+
if (typeof window !== 'undefined') {
|
|
79
|
+
window.scrollTo(0, 0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let destinationId: number | null = null;
|
|
83
|
+
let destinationIsCountry = false;
|
|
84
|
+
let destinationIsRegion = false;
|
|
85
|
+
let destinationIsOord = false;
|
|
86
|
+
let destinationIsLocation = false;
|
|
87
|
+
|
|
88
|
+
if (country) {
|
|
89
|
+
destinationId = country;
|
|
90
|
+
destinationIsCountry = true;
|
|
91
|
+
} else if (region) {
|
|
92
|
+
destinationId = region;
|
|
93
|
+
destinationIsRegion = true;
|
|
94
|
+
} else if (oord) {
|
|
95
|
+
destinationId = oord;
|
|
96
|
+
destinationIsOord = true;
|
|
97
|
+
} else if (city) {
|
|
98
|
+
destinationId = city;
|
|
99
|
+
destinationIsLocation = true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
var searchRequest = {
|
|
103
|
+
officeId: 1,
|
|
104
|
+
payload: {
|
|
105
|
+
catalogueIds: context!.tideConnection.catalogueIds ?? [],
|
|
106
|
+
serviceType: context!.type === 'hotel' || context!.type === 'hotel-flight' ? 3 : context!.type === 'flight' ? 7 : context!.type === 'roundTrip' ? 1 : 0,
|
|
107
|
+
searchType: 0,
|
|
108
|
+
destination: {
|
|
109
|
+
id: Number(destinationId),
|
|
110
|
+
isCountry: destinationIsCountry,
|
|
111
|
+
isRegion: destinationIsRegion,
|
|
112
|
+
isOord: destinationIsOord,
|
|
113
|
+
isLocation: destinationIsLocation
|
|
114
|
+
} as BookingPackageDestination,
|
|
115
|
+
rooms: getRequestRoomsFromEntry(rooms),
|
|
116
|
+
fromDate: from,
|
|
117
|
+
toDate: to,
|
|
118
|
+
earliestFromOffset: 0,
|
|
119
|
+
latestToOffset: 0,
|
|
120
|
+
includeFlights: true,
|
|
121
|
+
regimeCodes: entry.items.map((i) => i.regimeCode) || [],
|
|
122
|
+
useExactDates: true,
|
|
123
|
+
onlyCachedResults: false,
|
|
124
|
+
includeAllAllotments: true,
|
|
125
|
+
productCodes: hotel ? [hotel] : []
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return searchRequest;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const buildSearchFromQueryParams = (params: URLSearchParams): BookingPackageRequest<BookingPackageSearchRequest> => {
|
|
133
|
+
let from = getDateFromParams(params, 'fromDate');
|
|
134
|
+
let to = getDateFromParams(params, 'toDate');
|
|
135
|
+
const rooms = getRoomsFromParams(params, 'rooms');
|
|
136
|
+
let country = getNumberFromParams(params, 'country');
|
|
137
|
+
let region = getNumberFromParams(params, 'region');
|
|
138
|
+
let oord = getNumberFromParams(params, 'oord');
|
|
139
|
+
let city = getNumberFromParams(params, 'location');
|
|
140
|
+
let hotel = getNumberFromParams(params, 'hotel');
|
|
141
|
+
let tagId = getNumberFromParams(params, 'tagId');
|
|
142
|
+
|
|
143
|
+
// temp hardcoded params
|
|
144
|
+
if (!from || !to) {
|
|
145
|
+
from = '2026-04-07';
|
|
146
|
+
to = '2026-04-13';
|
|
147
|
+
}
|
|
148
|
+
if (!country && !region && !oord && !city) {
|
|
149
|
+
region = 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof window !== 'undefined') {
|
|
153
|
+
window.scrollTo(0, 0);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let destinationId: number | null = null;
|
|
157
|
+
let destinationIsCountry = false;
|
|
158
|
+
let destinationIsRegion = false;
|
|
159
|
+
let destinationIsOord = false;
|
|
160
|
+
let destinationIsLocation = false;
|
|
161
|
+
|
|
162
|
+
if (country) {
|
|
163
|
+
destinationId = country;
|
|
164
|
+
destinationIsCountry = true;
|
|
165
|
+
} else if (region) {
|
|
166
|
+
destinationId = region;
|
|
167
|
+
destinationIsRegion = true;
|
|
168
|
+
} else if (oord) {
|
|
169
|
+
destinationId = oord;
|
|
170
|
+
destinationIsOord = true;
|
|
171
|
+
} else if (city) {
|
|
172
|
+
destinationId = city;
|
|
173
|
+
destinationIsLocation = true;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
var searchRequest = {
|
|
177
|
+
officeId: 1,
|
|
178
|
+
payload: {
|
|
179
|
+
catalogueIds: context!.tideConnection.catalogueIds ?? [],
|
|
180
|
+
serviceType: context!.type === 'hotel' || context!.type === 'hotel-flight' ? 3 : context!.type === 'flight' ? 7 : context!.type === 'roundTrip' ? 1 : 0,
|
|
181
|
+
searchType: 0,
|
|
182
|
+
destination: {
|
|
183
|
+
id: Number(destinationId),
|
|
184
|
+
isCountry: destinationIsCountry,
|
|
185
|
+
isRegion: destinationIsRegion,
|
|
186
|
+
isOord: destinationIsOord,
|
|
187
|
+
isLocation: destinationIsLocation
|
|
188
|
+
} as BookingPackageDestination,
|
|
189
|
+
rooms: getRequestRooms(rooms),
|
|
190
|
+
fromDate: from,
|
|
191
|
+
toDate: to,
|
|
192
|
+
earliestFromOffset: 0,
|
|
193
|
+
latestToOffset: 0,
|
|
194
|
+
includeFlights: true,
|
|
195
|
+
regimeCodes:
|
|
196
|
+
filters
|
|
197
|
+
.find((f) => f.property === 'regime')
|
|
198
|
+
?.options?.filter((o) => o.isChecked)
|
|
199
|
+
.flatMap((o) => o.value.toString()) || [],
|
|
200
|
+
minPrice: filters.find((f) => f.property === 'price')?.selectedMin,
|
|
201
|
+
maxPrice: filters.find((f) => f.property === 'price')?.selectedMax,
|
|
202
|
+
useExactDates: true,
|
|
203
|
+
onlyCachedResults: false,
|
|
204
|
+
includeAllAllotments: true,
|
|
205
|
+
productIds: hotel ? [hotel] : [],
|
|
206
|
+
productTagIds: tagId ? [tagId] : []
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return searchRequest;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const getRequestRoomsFromEntry = (rooms: EntryRoom[] | null) => {
|
|
214
|
+
if (!rooms) {
|
|
215
|
+
// Fall back to 2 adults
|
|
216
|
+
var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
|
|
217
|
+
range(0, 2).forEach(() => {
|
|
218
|
+
room.pax.push({
|
|
219
|
+
age: 30
|
|
220
|
+
} as BookingPackagePax);
|
|
221
|
+
});
|
|
222
|
+
return [room];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const requestRooms = rooms?.map((x, i) => {
|
|
226
|
+
var room = { index: i, pax: [] } as BookingPackageRequestRoom;
|
|
227
|
+
x.travellers.forEach((p) => {
|
|
228
|
+
room.pax.push({
|
|
229
|
+
age: p.age,
|
|
230
|
+
dateOfBirth: p.dateOfBirth
|
|
231
|
+
} as BookingPackagePax);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return room;
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return requestRooms;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const getRequestRooms = (rooms: Room[] | null) => {
|
|
241
|
+
if (!rooms) {
|
|
242
|
+
// Fall back to 2 adults
|
|
243
|
+
var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
|
|
244
|
+
range(0, 2).forEach(() => {
|
|
245
|
+
room.pax.push({
|
|
246
|
+
age: 30
|
|
247
|
+
} as BookingPackagePax);
|
|
248
|
+
});
|
|
249
|
+
return [room];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const requestRooms = rooms?.map((x, i) => {
|
|
253
|
+
var room = { index: i, pax: [] } as BookingPackageRequestRoom;
|
|
254
|
+
range(0, x.adults).forEach(() => {
|
|
255
|
+
room.pax.push({
|
|
256
|
+
age: 30
|
|
257
|
+
} as BookingPackagePax);
|
|
258
|
+
});
|
|
259
|
+
x.childAges.forEach((x) => {
|
|
260
|
+
room.pax.push({
|
|
261
|
+
age: x
|
|
262
|
+
} as BookingPackagePax);
|
|
263
|
+
});
|
|
264
|
+
return room;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return requestRooms;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
if (typeof document !== 'undefined') {
|
|
272
|
+
document.body.classList.toggle('has-overlay', filtersOpen);
|
|
273
|
+
}
|
|
274
|
+
}, [filtersOpen]);
|
|
43
275
|
|
|
44
276
|
// seperate Search
|
|
45
277
|
useEffect(() => {
|
|
@@ -50,90 +282,27 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
50
282
|
return;
|
|
51
283
|
}
|
|
52
284
|
|
|
53
|
-
const params = new URLSearchParams(location.search);
|
|
54
|
-
let from = getDateFromParams(params, 'fromDate');
|
|
55
|
-
let to = getDateFromParams(params, 'toDate');
|
|
56
|
-
const rooms = getRoomsFromParams(params, 'rooms');
|
|
57
|
-
let country = getNumberFromParams(params, 'country');
|
|
58
|
-
let region = getNumberFromParams(params, 'region');
|
|
59
|
-
let oord = getNumberFromParams(params, 'oord');
|
|
60
|
-
let city = getNumberFromParams(params, 'location');
|
|
61
|
-
let hotel = getNumberFromParams(params, 'hotel');
|
|
62
|
-
let tagId = getNumberFromParams(params, 'tagId');
|
|
63
|
-
|
|
64
|
-
// temp hardcoded params
|
|
65
|
-
if (!from || !to) {
|
|
66
|
-
from = '2026-04-07';
|
|
67
|
-
to = '2026-04-13';
|
|
68
|
-
}
|
|
69
|
-
if (!country && !region && !oord && !city) {
|
|
70
|
-
region = 1;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (typeof window !== 'undefined') {
|
|
74
|
-
window.scrollTo(0, 0);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let destinationId: number | null = null;
|
|
78
|
-
let destinationIsCountry = false;
|
|
79
|
-
let destinationIsRegion = false;
|
|
80
|
-
let destinationIsOord = false;
|
|
81
|
-
let destinationIsLocation = false;
|
|
82
|
-
|
|
83
|
-
if (country) {
|
|
84
|
-
destinationId = country;
|
|
85
|
-
destinationIsCountry = true;
|
|
86
|
-
} else if (region) {
|
|
87
|
-
destinationId = region;
|
|
88
|
-
destinationIsRegion = true;
|
|
89
|
-
} else if (oord) {
|
|
90
|
-
destinationId = oord;
|
|
91
|
-
destinationIsOord = true;
|
|
92
|
-
} else if (city) {
|
|
93
|
-
destinationId = city;
|
|
94
|
-
destinationIsLocation = true;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const searchRequest: BookingPackageRequest<BookingPackageSearchRequest> = {
|
|
98
|
-
officeId: 1,
|
|
99
|
-
payload: {
|
|
100
|
-
catalogueIds: context.tideConnection.catalogueIds ?? [],
|
|
101
|
-
serviceType:
|
|
102
|
-
context?.type === 'hotel' || context?.type === 'hotel-flight' ? 3 : context?.type === 'flight' ? 7 : context?.type === 'roundTrip' ? 1 : 0,
|
|
103
|
-
searchType: 0,
|
|
104
|
-
destination: {
|
|
105
|
-
id: Number(destinationId),
|
|
106
|
-
isCountry: destinationIsCountry,
|
|
107
|
-
isRegion: destinationIsRegion,
|
|
108
|
-
isOord: destinationIsOord,
|
|
109
|
-
isLocation: destinationIsLocation
|
|
110
|
-
} as BookingPackageDestination,
|
|
111
|
-
rooms: getRequestRooms(rooms),
|
|
112
|
-
fromDate: from,
|
|
113
|
-
toDate: to,
|
|
114
|
-
earliestFromOffset: 0,
|
|
115
|
-
latestToOffset: 0,
|
|
116
|
-
includeFlights: true,
|
|
117
|
-
regimeCodes:
|
|
118
|
-
filters
|
|
119
|
-
.find((f) => f.property === 'regime')
|
|
120
|
-
?.options?.filter((o) => o.isChecked)
|
|
121
|
-
.flatMap((o) => o.value.toString()) || [],
|
|
122
|
-
minPrice: filters.find((f) => f.property === 'price')?.selectedMin,
|
|
123
|
-
maxPrice: filters.find((f) => f.property === 'price')?.selectedMax,
|
|
124
|
-
useExactDates: true,
|
|
125
|
-
onlyCachedResults: false,
|
|
126
|
-
includeAllAllotments: true,
|
|
127
|
-
productIds: hotel ? [hotel] : [],
|
|
128
|
-
productTagIds: tagId ? [tagId] : []
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
|
|
132
285
|
const config: TideClientConfig = {
|
|
133
286
|
host: context.tideConnection.host,
|
|
134
287
|
apiKey: context.tideConnection.apiKey
|
|
135
288
|
};
|
|
136
289
|
|
|
290
|
+
const params = new URLSearchParams(location.search);
|
|
291
|
+
let entryId = getStringFromParams(params, 'entryId');
|
|
292
|
+
let entryLight: EntryLight | null = null;
|
|
293
|
+
// If entryId is present, we want to fetch the details of that entry and use the details to populate the search results, instead of running a search with the other query params
|
|
294
|
+
|
|
295
|
+
let searchRequest: BookingPackageRequest<BookingPackageSearchRequest>;
|
|
296
|
+
|
|
297
|
+
if (entryId) {
|
|
298
|
+
entryLight = await getEntryLight(config, entryId);
|
|
299
|
+
// populate itinerary store
|
|
300
|
+
dispatch(setEntry({ entry: entryLight }));
|
|
301
|
+
searchRequest = buildSearchFromEntry(entryLight);
|
|
302
|
+
} else {
|
|
303
|
+
searchRequest = buildSearchFromQueryParams(params);
|
|
304
|
+
}
|
|
305
|
+
|
|
137
306
|
const packageSearchResults = await search(config, searchRequest);
|
|
138
307
|
|
|
139
308
|
console.log('Search results', packageSearchResults);
|
|
@@ -147,8 +316,17 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
147
316
|
|
|
148
317
|
dispatch(setResults({ results: packageSearchResults }));
|
|
149
318
|
if (packageSearchResults?.length > 0) {
|
|
150
|
-
|
|
319
|
+
if (entryId) {
|
|
320
|
+
const matching = packageSearchResults.find((r) => r.productId === entry?.id);
|
|
321
|
+
|
|
322
|
+
if (matching) {
|
|
323
|
+
dispatch(setSelectedHotel(matching.productId));
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
dispatch(setSelectedHotel(packageSearchResults[0]?.productId));
|
|
327
|
+
}
|
|
151
328
|
}
|
|
329
|
+
|
|
152
330
|
dispatch(setIsLoading(false));
|
|
153
331
|
} catch (err) {
|
|
154
332
|
console.error('Search failed', err);
|
|
@@ -156,7 +334,9 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
156
334
|
}
|
|
157
335
|
};
|
|
158
336
|
|
|
159
|
-
|
|
337
|
+
if (!context?.showMockup) {
|
|
338
|
+
runSearch();
|
|
339
|
+
}
|
|
160
340
|
}, [location.search, searchTrigger]);
|
|
161
341
|
|
|
162
342
|
// Seperate detailsCall
|
|
@@ -171,11 +351,21 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
171
351
|
};
|
|
172
352
|
|
|
173
353
|
const selectedItem = results.find((r) => r.productId === selectedHotelId);
|
|
174
|
-
if (!selectedItem)
|
|
354
|
+
if (!selectedItem) {
|
|
355
|
+
// TODO: handle this case better, show an error message to the user
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
175
358
|
|
|
176
359
|
const params = new URLSearchParams(location.search);
|
|
177
|
-
|
|
178
|
-
|
|
360
|
+
let entryId = getStringFromParams(params, 'entryId');
|
|
361
|
+
|
|
362
|
+
let requestRooms;
|
|
363
|
+
if (entry && entryId) {
|
|
364
|
+
requestRooms = getRequestRoomsFromEntry(entry.rooms);
|
|
365
|
+
} else {
|
|
366
|
+
const rooms = getRoomsFromParams(params, 'rooms');
|
|
367
|
+
requestRooms = getRequestRooms(rooms);
|
|
368
|
+
}
|
|
179
369
|
|
|
180
370
|
const detailsRequest: BookingPackageRequest<BookingPackageDetailsRequest> = {
|
|
181
371
|
officeId: 1,
|
|
@@ -197,10 +387,17 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
197
387
|
}
|
|
198
388
|
};
|
|
199
389
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
390
|
+
if (entry && entryId) {
|
|
391
|
+
requestRooms = getRequestRoomsFromEntry(entry.rooms);
|
|
392
|
+
const detailsResponse = await details(config, detailsRequest);
|
|
393
|
+
console.log('Details:', detailsResponse);
|
|
394
|
+
dispatch(setBookingPackageDetails({ details: detailsResponse?.payload }));
|
|
395
|
+
} else {
|
|
396
|
+
const detailsWLResponse = await detailsWL(config, detailsRequest);
|
|
397
|
+
console.log('Details with entryLight:', detailsWLResponse);
|
|
398
|
+
dispatch(setBookingPackageDetails({ details: detailsWLResponse?.payload?.bookingPackage }));
|
|
399
|
+
dispatch(setEntry({ entry: detailsWLResponse?.payload?.entry }));
|
|
400
|
+
}
|
|
204
401
|
} catch (err) {
|
|
205
402
|
console.error('Failed to fetch package details', err);
|
|
206
403
|
}
|
|
@@ -209,55 +406,6 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
209
406
|
fetchPackageDetails();
|
|
210
407
|
}, [selectedHotelId]);
|
|
211
408
|
|
|
212
|
-
const getRequestRooms = (rooms: Room[] | null) => {
|
|
213
|
-
if (!rooms) {
|
|
214
|
-
// Fall back to 2 adults
|
|
215
|
-
var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
|
|
216
|
-
range(0, 2).forEach(() => {
|
|
217
|
-
room.pax.push({
|
|
218
|
-
age: 30
|
|
219
|
-
} as BookingPackagePax);
|
|
220
|
-
});
|
|
221
|
-
return [room];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const requestRooms = rooms?.map((x, i) => {
|
|
225
|
-
var room = { index: i, pax: [] } as BookingPackageRequestRoom;
|
|
226
|
-
range(0, x.adults).forEach(() => {
|
|
227
|
-
room.pax.push({
|
|
228
|
-
age: 30
|
|
229
|
-
} as BookingPackagePax);
|
|
230
|
-
});
|
|
231
|
-
x.childAges.forEach((x) => {
|
|
232
|
-
room.pax.push({
|
|
233
|
-
age: x
|
|
234
|
-
} as BookingPackagePax);
|
|
235
|
-
});
|
|
236
|
-
return room;
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
return requestRooms;
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
243
|
-
const [itineraryOpen, setItineraryOpen] = useState(false);
|
|
244
|
-
|
|
245
|
-
const handleSortChange = (newSortKey: string) => {
|
|
246
|
-
dispatch(setSortKey(newSortKey));
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
useEffect(() => {
|
|
250
|
-
if (typeof document !== 'undefined') {
|
|
251
|
-
document.body.classList.toggle('has-overlay', filtersOpen);
|
|
252
|
-
}
|
|
253
|
-
}, [filtersOpen]);
|
|
254
|
-
|
|
255
|
-
const sortingOptions: SortingOption[] = [
|
|
256
|
-
{ key: 'price-asc', label: translations.SRP.PRICE_ASC },
|
|
257
|
-
{ key: 'price-desc', label: translations.SRP.PRICE_DESC },
|
|
258
|
-
{ key: 'departure-date', label: translations.SRP.DEPARTURE_ASC }
|
|
259
|
-
];
|
|
260
|
-
|
|
261
409
|
return (
|
|
262
410
|
<div id="tide-booking" className="search__bg">
|
|
263
411
|
{context && (
|
|
@@ -329,17 +477,17 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
329
477
|
<div className="search__results__wrapper">
|
|
330
478
|
{context.showTabViews && <TabViews />}
|
|
331
479
|
|
|
332
|
-
{
|
|
480
|
+
{context.showRoundTripResults && context.showMockup && <RoundTripResults />}
|
|
333
481
|
|
|
334
482
|
{context.showFlightResults && bookingPackageDetails?.outwardFlights && (
|
|
335
|
-
<FlightResults flights={bookingPackageDetails
|
|
483
|
+
<FlightResults flights={bookingPackageDetails?.outwardFlights} isDeparture={true} />
|
|
336
484
|
)}
|
|
337
485
|
|
|
338
486
|
{context.showHotelAccommodationResults && <HotelAccommodationResults isLoading={isLoading} context={context} />}
|
|
339
|
-
{
|
|
487
|
+
{context.showFlightAccommodationResults && context.showMockup && <FlightAccommodationResults />}
|
|
340
488
|
|
|
341
489
|
{context.showFlightResults && bookingPackageDetails?.returnFlights && (
|
|
342
|
-
<FlightResults flights={bookingPackageDetails
|
|
490
|
+
<FlightResults flights={bookingPackageDetails?.returnFlights} isDeparture={false} />
|
|
343
491
|
)}
|
|
344
492
|
</div>
|
|
345
493
|
</div>
|
|
@@ -248,7 +248,11 @@ const FlightSearchResultsSelfContained: React.FC<FlightSearchResultsSelfContaine
|
|
|
248
248
|
isLoading: false,
|
|
249
249
|
customSpinner: null,
|
|
250
250
|
|
|
251
|
-
languageCode: 'en-GB'
|
|
251
|
+
languageCode: 'en-GB',
|
|
252
|
+
destinationImage: {
|
|
253
|
+
url: 'https://images.unsplash.com/photo-1534292425621-6fbc5c09219a?fm=jpg&q=60&w=3000&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8dGVuZXJpZmV8ZW58MHx8MHx8fDA%3D',
|
|
254
|
+
alt: 'Tenerife'
|
|
255
|
+
}
|
|
252
256
|
};
|
|
253
257
|
|
|
254
258
|
return (
|
|
@@ -97,7 +97,11 @@ const HotelFlightSearchResultsSelfContained: React.FC<HotelFlightSearchResultsSe
|
|
|
97
97
|
showFlightResults: true,
|
|
98
98
|
showHotelAccommodationResults: true,
|
|
99
99
|
showCustomCards: true,
|
|
100
|
-
customCardRenderer
|
|
100
|
+
customCardRenderer,
|
|
101
|
+
destinationImage: {
|
|
102
|
+
url: 'https://images.unsplash.com/photo-1534292425621-6fbc5c09219a?fm=jpg&q=60&w=3000&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8dGVuZXJpZmV8ZW58MHx8MHx8fDA%3D',
|
|
103
|
+
alt: 'Tenerife'
|
|
104
|
+
}
|
|
101
105
|
};
|
|
102
106
|
|
|
103
107
|
return (
|
|
@@ -174,7 +174,11 @@ const HotelSearchResultsSelfContained: React.FC<HotelSearchResultsSelfContainedP
|
|
|
174
174
|
isLoading: false,
|
|
175
175
|
customSpinner: null,
|
|
176
176
|
|
|
177
|
-
languageCode: 'en-GB'
|
|
177
|
+
languageCode: 'en-GB',
|
|
178
|
+
destinationImage: {
|
|
179
|
+
url: 'https://images.unsplash.com/photo-1534292425621-6fbc5c09219a?fm=jpg&q=60&w=3000&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8dGVuZXJpZmV8ZW58MHx8MHx8fDA%3D',
|
|
180
|
+
alt: 'Tenerife'
|
|
181
|
+
}
|
|
178
182
|
};
|
|
179
183
|
|
|
180
184
|
return (
|
|
@@ -19,7 +19,11 @@ const RoundtripSearchResultsSelfContained: React.FC<RoundtripSearchResultsSelfCo
|
|
|
19
19
|
showFlightResults: false,
|
|
20
20
|
showHotelAccommodationResults: false,
|
|
21
21
|
showRoundTripResults: true,
|
|
22
|
-
showCustomCards: false
|
|
22
|
+
showCustomCards: false,
|
|
23
|
+
destinationImage: {
|
|
24
|
+
url: 'https://images.unsplash.com/photo-1534292425621-6fbc5c09219a?fm=jpg&q=60&w=3000&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8dGVuZXJpZmV8ZW58MHx8MHx8fDA%3D',
|
|
25
|
+
alt: 'Tenerife'
|
|
26
|
+
}
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
return (
|
|
@@ -27,7 +27,7 @@ export interface SearchResultsConfiguration {
|
|
|
27
27
|
showRoundTripResults?: boolean;
|
|
28
28
|
showCustomCards?: boolean;
|
|
29
29
|
customCardRenderer?: (result: SearchResult) => ReactNode;
|
|
30
|
-
|
|
30
|
+
showMockup?: boolean;
|
|
31
31
|
// Map view
|
|
32
32
|
// not supported for now
|
|
33
33
|
showMapView?: boolean;
|
|
@@ -42,6 +42,8 @@ export interface SearchResultsConfiguration {
|
|
|
42
42
|
|
|
43
43
|
cmsHotelData?: any[];
|
|
44
44
|
languageCode?: string;
|
|
45
|
+
|
|
46
|
+
destinationImage?: { url: string; alt: string };
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
export type FilterType = 'checkbox' | 'toggle' | 'slider' | 'star-rating';
|