@qite/tide-booking-component 1.4.98 → 1.4.100
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 +1183 -669
- package/build/build-cjs/src/search-results/components/itinerary/index.d.ts +2 -0
- package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +11 -10
- package/build/build-cjs/src/search-results/types.d.ts +23 -1
- package/build/build-cjs/src/search-results/utils/packaging-utils.d.ts +7 -0
- package/build/build-cjs/src/search-results/utils/query-utils.d.ts +11 -0
- package/build/build-cjs/src/shared/components/flyin/accommodation-flyin.d.ts +1 -2
- package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +4 -0
- package/build/build-cjs/src/shared/utils/localization-util.d.ts +1 -0
- package/build/build-esm/index.js +1183 -669
- package/build/build-esm/src/search-results/components/itinerary/index.d.ts +2 -0
- package/build/build-esm/src/search-results/store/search-results-slice.d.ts +11 -10
- package/build/build-esm/src/search-results/types.d.ts +23 -1
- package/build/build-esm/src/search-results/utils/packaging-utils.d.ts +7 -0
- package/build/build-esm/src/search-results/utils/query-utils.d.ts +11 -0
- package/build/build-esm/src/shared/components/flyin/accommodation-flyin.d.ts +1 -2
- package/build/build-esm/src/shared/components/flyin/flyin.d.ts +4 -0
- package/build/build-esm/src/shared/utils/localization-util.d.ts +1 -0
- package/package.json +2 -2
- package/src/qsm/components/search-input-group/index.tsx +0 -1
- package/src/search-results/components/itinerary/index.tsx +150 -81
- package/src/search-results/components/search-results-container/search-results-container.tsx +463 -384
- package/src/search-results/store/search-results-slice.ts +28 -10
- package/src/search-results/types.ts +25 -1
- package/src/search-results/utils/packaging-utils.ts +75 -0
- package/src/search-results/utils/query-utils.ts +152 -0
- package/src/shared/components/flyin/accommodation-flyin.tsx +10 -11
- package/src/shared/components/flyin/flyin.tsx +52 -4
- package/styles/components/_flyin.scss +25 -0
- package/styles/components/_search.scss +26 -1
|
@@ -9,21 +9,22 @@ import {
|
|
|
9
9
|
setIsLoading,
|
|
10
10
|
setSelectedSearchResult,
|
|
11
11
|
setBookingPackageDetails,
|
|
12
|
-
setEntry,
|
|
13
12
|
setFlyInIsOpen,
|
|
14
13
|
setFilteredResults,
|
|
15
14
|
setPackagingAccoResults,
|
|
16
15
|
setFilteredPackagingAccoResults,
|
|
17
|
-
setPackagingAccoSearchDetails
|
|
16
|
+
setPackagingAccoSearchDetails,
|
|
17
|
+
setEditablePackagingEntry,
|
|
18
|
+
setTransactionId,
|
|
19
|
+
setAccommodationFlyInStep,
|
|
20
|
+
setPriceDetails
|
|
18
21
|
} from '../../store/search-results-slice';
|
|
19
|
-
import { Filter, SortByType } from '../../types';
|
|
22
|
+
import { AccommodationFlyInStep, Filter, SearchSeed, SortByType } from '../../types';
|
|
20
23
|
import useMediaQuery from '../../../shared/utils/use-media-query-util';
|
|
21
24
|
import ItemPicker from '../item-picker';
|
|
22
25
|
import {
|
|
23
26
|
TideClientConfig,
|
|
24
27
|
details,
|
|
25
|
-
detailsWL,
|
|
26
|
-
getEntryLight,
|
|
27
28
|
search,
|
|
28
29
|
searchPackagingAccommodations,
|
|
29
30
|
BookingPackageDestination,
|
|
@@ -32,16 +33,16 @@ import {
|
|
|
32
33
|
BookingPackageRequest,
|
|
33
34
|
BookingPackageRequestRoom,
|
|
34
35
|
BookingPackageSearchRequest,
|
|
35
|
-
EntryLight,
|
|
36
|
-
EntryRoom,
|
|
37
36
|
PackagingAccommodationRequest,
|
|
38
37
|
PackagingDestination,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
PortalQsmType,
|
|
39
|
+
PackagingEntry,
|
|
40
|
+
startTransaction,
|
|
41
|
+
PackagingEntryLine,
|
|
42
|
+
getPriceDetails
|
|
42
43
|
} from '@qite/tide-client';
|
|
43
44
|
import { getDateFromParams, getNumberFromParams, getRoomsFromParams, getStringFromParams } from '../../../shared/utils/query-string-util';
|
|
44
|
-
import { range } from 'lodash';
|
|
45
|
+
import { first, last, range } from 'lodash';
|
|
45
46
|
import { Room } from '../../../booking-wizard/types';
|
|
46
47
|
import Icon from '../icon';
|
|
47
48
|
import Itinerary from '../itinerary';
|
|
@@ -61,8 +62,22 @@ import {
|
|
|
61
62
|
enrichFiltersWithPackageAccoResults,
|
|
62
63
|
enrichFiltersWithResults
|
|
63
64
|
} from '../../utils/search-results-utils';
|
|
65
|
+
import {
|
|
66
|
+
ACCOMMODATION_SERVICE_TYPE,
|
|
67
|
+
FLIGHT_SERVICE_TYPE,
|
|
68
|
+
getDepartureAirportFromEntry,
|
|
69
|
+
getDestinationAirportFromEntry,
|
|
70
|
+
getPackagingRequestRoomsFromBookingRooms,
|
|
71
|
+
getRequestRoomsFromPackagingEntry,
|
|
72
|
+
GROUP_TOUR_SERVICE_TYPE,
|
|
73
|
+
parseHotelId,
|
|
74
|
+
toDateOnlyString
|
|
75
|
+
} from '../../utils/query-utils';
|
|
76
|
+
import { getRequestRoomsFromPackagingSegments, getRoomIndexFromLine, getSelectedOptionsPerRoom } from '../../utils/packaging-utils';
|
|
64
77
|
|
|
65
78
|
const SearchResultsContainer: React.FC = () => {
|
|
79
|
+
const currentSearch = typeof window !== 'undefined' ? window.location.search : '';
|
|
80
|
+
|
|
66
81
|
const dispatch = useDispatch();
|
|
67
82
|
|
|
68
83
|
const context = useContext(SearchResultsConfigurationContext);
|
|
@@ -74,13 +89,16 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
74
89
|
packagingAccoResults,
|
|
75
90
|
filteredPackagingAccoResults,
|
|
76
91
|
bookingPackageDetails,
|
|
77
|
-
entry,
|
|
78
92
|
isLoading,
|
|
79
93
|
filters,
|
|
80
94
|
selectedSortType,
|
|
81
95
|
selectedSearchResult,
|
|
82
96
|
selectedPackagingAccoResultCode,
|
|
83
|
-
flyInIsOpen
|
|
97
|
+
flyInIsOpen,
|
|
98
|
+
packagingAccoSearchDetails,
|
|
99
|
+
editablePackagingEntry,
|
|
100
|
+
transactionId,
|
|
101
|
+
accommodationFlyInStep
|
|
84
102
|
} = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
85
103
|
|
|
86
104
|
const isMobile = useMediaQuery('(max-width: 1200px)');
|
|
@@ -90,9 +108,12 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
90
108
|
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
91
109
|
|
|
92
110
|
const [detailsIsLoading, setDetailsIsLoading] = useState(false);
|
|
111
|
+
const [pricesAreLoading, setPricesAreLoading] = useState(false);
|
|
93
112
|
|
|
94
113
|
const [itineraryOpen, setItineraryOpen] = useState(false);
|
|
95
114
|
|
|
115
|
+
const [selectedAccommodationSeed, setSelectedAccommodationSeed] = useState<SearchSeed | null>(null);
|
|
116
|
+
|
|
96
117
|
const panelRef = useRef<HTMLDivElement | null>(null);
|
|
97
118
|
|
|
98
119
|
const sortByTypes: SortByType[] = [
|
|
@@ -112,96 +133,43 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
112
133
|
}
|
|
113
134
|
};
|
|
114
135
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
let city = hotelItem ? hotelItem.locationId : null;
|
|
126
|
-
let hotel = hotelItem ? hotelItem.productCode : null;
|
|
127
|
-
|
|
128
|
-
if (typeof window !== 'undefined') {
|
|
129
|
-
window.scrollTo(0, 0);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
let destinationId: number | null = null;
|
|
133
|
-
let destinationIsCountry = false;
|
|
134
|
-
let destinationIsRegion = false;
|
|
135
|
-
let destinationIsOord = false;
|
|
136
|
-
let destinationIsLocation = false;
|
|
137
|
-
|
|
138
|
-
if (country) {
|
|
139
|
-
destinationId = country;
|
|
140
|
-
destinationIsCountry = true;
|
|
141
|
-
} else if (region) {
|
|
142
|
-
destinationId = region;
|
|
143
|
-
destinationIsRegion = true;
|
|
144
|
-
} else if (oord) {
|
|
145
|
-
destinationId = oord;
|
|
146
|
-
destinationIsOord = true;
|
|
147
|
-
} else if (city) {
|
|
148
|
-
destinationId = city;
|
|
149
|
-
destinationIsLocation = true;
|
|
136
|
+
const getRequestRooms = (rooms: Room[] | null) => {
|
|
137
|
+
if (!rooms) {
|
|
138
|
+
// Fall back to 2 adults
|
|
139
|
+
var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
|
|
140
|
+
range(0, 2).forEach(() => {
|
|
141
|
+
room.pax.push({
|
|
142
|
+
age: 30
|
|
143
|
+
} as BookingPackagePax);
|
|
144
|
+
});
|
|
145
|
+
return [room];
|
|
150
146
|
}
|
|
151
147
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
destination: {
|
|
167
|
-
id: Number(destinationId),
|
|
168
|
-
isCountry: destinationIsCountry,
|
|
169
|
-
isRegion: destinationIsRegion,
|
|
170
|
-
isOord: destinationIsOord,
|
|
171
|
-
isLocation: destinationIsLocation
|
|
172
|
-
} as BookingPackageDestination,
|
|
173
|
-
rooms: getRequestRoomsFromEntry(rooms),
|
|
174
|
-
fromDate: from,
|
|
175
|
-
toDate: to,
|
|
176
|
-
earliestFromOffset: 0,
|
|
177
|
-
latestToOffset: 0,
|
|
178
|
-
includeFlights: true,
|
|
179
|
-
regimeCodes: entry.items.map((i) => i.regimeCode) || [],
|
|
180
|
-
useExactDates: true,
|
|
181
|
-
onlyCachedResults: false,
|
|
182
|
-
includeAllAllotments: true,
|
|
183
|
-
productCodes: hotel ? [hotel] : []
|
|
184
|
-
}
|
|
185
|
-
};
|
|
148
|
+
const requestRooms = rooms?.map((x, i) => {
|
|
149
|
+
var room = { index: i, pax: [] } as BookingPackageRequestRoom;
|
|
150
|
+
range(0, x.adults).forEach(() => {
|
|
151
|
+
room.pax.push({
|
|
152
|
+
age: 30
|
|
153
|
+
} as BookingPackagePax);
|
|
154
|
+
});
|
|
155
|
+
x.childAges.forEach((x) => {
|
|
156
|
+
room.pax.push({
|
|
157
|
+
age: x
|
|
158
|
+
} as BookingPackagePax);
|
|
159
|
+
});
|
|
160
|
+
return room;
|
|
161
|
+
});
|
|
186
162
|
|
|
187
|
-
return
|
|
163
|
+
return requestRooms;
|
|
188
164
|
};
|
|
189
165
|
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
let city = getNumberFromParams(params, 'location');
|
|
198
|
-
let hotel = getNumberFromParams(params, 'hotel');
|
|
199
|
-
let tagId = getNumberFromParams(params, 'tagId');
|
|
200
|
-
|
|
201
|
-
if (!from || !to) {
|
|
202
|
-
console.error('Missing fromDate or toDate in query params, using default values');
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
166
|
+
const buildSearchFromSeed = (seed: SearchSeed): BookingPackageRequest<BookingPackageSearchRequest> => {
|
|
167
|
+
const country = seed.country;
|
|
168
|
+
const region = seed.region;
|
|
169
|
+
const oord = seed.oord;
|
|
170
|
+
const city = seed.location;
|
|
171
|
+
const hotel = seed.hotel;
|
|
172
|
+
const tagId = seed.tagId;
|
|
205
173
|
|
|
206
174
|
if (typeof window !== 'undefined') {
|
|
207
175
|
window.scrollTo(0, 0);
|
|
@@ -227,18 +195,18 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
227
195
|
destinationIsLocation = true;
|
|
228
196
|
}
|
|
229
197
|
|
|
230
|
-
|
|
198
|
+
return {
|
|
231
199
|
officeId: 1,
|
|
232
200
|
agentId: context?.agentId,
|
|
233
201
|
payload: {
|
|
234
202
|
catalogueIds: context!.tideConnection.catalogueIds ?? [],
|
|
235
203
|
serviceType:
|
|
236
204
|
context!.searchConfiguration.qsmType === PortalQsmType.Accommodation || context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight
|
|
237
|
-
?
|
|
205
|
+
? ACCOMMODATION_SERVICE_TYPE
|
|
238
206
|
: context!.searchConfiguration.qsmType === PortalQsmType.Flight
|
|
239
|
-
?
|
|
207
|
+
? FLIGHT_SERVICE_TYPE
|
|
240
208
|
: context!.searchConfiguration.qsmType === PortalQsmType.RoundTrip
|
|
241
|
-
?
|
|
209
|
+
? GROUP_TOUR_SERVICE_TYPE
|
|
242
210
|
: undefined,
|
|
243
211
|
searchType: context!.searchConfiguration.qsmType === PortalQsmType.GroupTour ? 1 : 0,
|
|
244
212
|
destination: {
|
|
@@ -248,19 +216,12 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
248
216
|
isOord: destinationIsOord,
|
|
249
217
|
isLocation: destinationIsLocation
|
|
250
218
|
} as BookingPackageDestination,
|
|
251
|
-
rooms:
|
|
252
|
-
fromDate:
|
|
253
|
-
toDate:
|
|
219
|
+
rooms: seed.rooms,
|
|
220
|
+
fromDate: seed.fromDate,
|
|
221
|
+
toDate: seed.toDate,
|
|
254
222
|
earliestFromOffset: 0,
|
|
255
223
|
latestToOffset: 0,
|
|
256
|
-
includeFlights: context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight
|
|
257
|
-
// regimeCodes:
|
|
258
|
-
// filters
|
|
259
|
-
// .find((f) => f.property === 'regime')
|
|
260
|
-
// ?.options?.filter((o) => o.isChecked)
|
|
261
|
-
// .flatMap((o) => o.value.toString()) || [],
|
|
262
|
-
// minPrice: filters.find((f) => f.property === 'price')?.selectedMin,
|
|
263
|
-
// maxPrice: filters.find((f) => f.property === 'price')?.selectedMax,
|
|
224
|
+
includeFlights: context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight,
|
|
264
225
|
useExactDates: context?.searchConfiguration.qsmType === PortalQsmType.GroupTour ? false : true,
|
|
265
226
|
onlyCachedResults: false,
|
|
266
227
|
includeAllAllotments: true,
|
|
@@ -268,86 +229,16 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
268
229
|
productTagIds: tagId ? [tagId] : []
|
|
269
230
|
}
|
|
270
231
|
};
|
|
271
|
-
|
|
272
|
-
console.log('Built search request from query params', searchRequest);
|
|
273
|
-
|
|
274
|
-
return searchRequest;
|
|
275
232
|
};
|
|
276
233
|
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
});
|
|
286
|
-
return [room];
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const requestRooms = rooms?.map((x, i) => {
|
|
290
|
-
var room = { index: i, pax: [] } as BookingPackageRequestRoom;
|
|
291
|
-
x.travellers.forEach((p) => {
|
|
292
|
-
room.pax.push({
|
|
293
|
-
age: p.age,
|
|
294
|
-
dateOfBirth: p.dateOfBirth
|
|
295
|
-
} as BookingPackagePax);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
return room;
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
return requestRooms;
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
const getRequestRooms = (rooms: Room[] | null) => {
|
|
305
|
-
if (!rooms) {
|
|
306
|
-
// Fall back to 2 adults
|
|
307
|
-
var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
|
|
308
|
-
range(0, 2).forEach(() => {
|
|
309
|
-
room.pax.push({
|
|
310
|
-
age: 30
|
|
311
|
-
} as BookingPackagePax);
|
|
312
|
-
});
|
|
313
|
-
return [room];
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const requestRooms = rooms?.map((x, i) => {
|
|
317
|
-
var room = { index: i, pax: [] } as BookingPackageRequestRoom;
|
|
318
|
-
range(0, x.adults).forEach(() => {
|
|
319
|
-
room.pax.push({
|
|
320
|
-
age: 30
|
|
321
|
-
} as BookingPackagePax);
|
|
322
|
-
});
|
|
323
|
-
x.childAges.forEach((x) => {
|
|
324
|
-
room.pax.push({
|
|
325
|
-
age: x
|
|
326
|
-
} as BookingPackagePax);
|
|
327
|
-
});
|
|
328
|
-
return room;
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
return requestRooms;
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
const buildPackagingAccommodationRequestFromQueryParams = (params: URLSearchParams): PackagingAccommodationRequest | null => {
|
|
335
|
-
let from = getDateFromParams(params, 'fromDate');
|
|
336
|
-
let to = getDateFromParams(params, 'toDate');
|
|
337
|
-
const rooms = getRoomsFromParams(params, 'rooms');
|
|
338
|
-
let country = getNumberFromParams(params, 'country');
|
|
339
|
-
let region = getNumberFromParams(params, 'region');
|
|
340
|
-
let oord = getNumberFromParams(params, 'oord');
|
|
341
|
-
let city = getNumberFromParams(params, 'location');
|
|
342
|
-
let hotel = getNumberFromParams(params, 'hotel');
|
|
343
|
-
let tagId = getNumberFromParams(params, 'tagId');
|
|
344
|
-
let agentId = getNumberFromParams(params, 'agentId');
|
|
345
|
-
let destinationAirport = getStringFromParams(params, 'destinationAirport');
|
|
346
|
-
|
|
347
|
-
if (!from || !to) {
|
|
348
|
-
console.error('Missing fromDate or toDate in query params, using default values');
|
|
349
|
-
return null;
|
|
350
|
-
}
|
|
234
|
+
const buildPackagingAccommodationRequestFromSeed = (seed: SearchSeed, currentTransactionId: string): PackagingAccommodationRequest => {
|
|
235
|
+
const country = seed.country;
|
|
236
|
+
const region = seed.region;
|
|
237
|
+
const oord = seed.oord;
|
|
238
|
+
const city = seed.location;
|
|
239
|
+
const hotelCode = seed.hotelCode ?? (seed.hotel ? seed.hotel.toString() : '');
|
|
240
|
+
const tagId = seed.tagId;
|
|
241
|
+
const destinationAirport = seed.destinationAirport;
|
|
351
242
|
|
|
352
243
|
if (typeof window !== 'undefined') {
|
|
353
244
|
window.scrollTo(0, 0);
|
|
@@ -361,32 +252,33 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
361
252
|
let destinationCode: string | null = null;
|
|
362
253
|
let destinationIsAirport = false;
|
|
363
254
|
|
|
364
|
-
if (
|
|
365
|
-
destinationId =
|
|
366
|
-
|
|
367
|
-
} else if (region) {
|
|
368
|
-
destinationId = region;
|
|
369
|
-
destinationIsRegion = true;
|
|
255
|
+
if (city) {
|
|
256
|
+
destinationId = city;
|
|
257
|
+
destinationIsLocation = true;
|
|
370
258
|
} else if (oord) {
|
|
371
259
|
destinationId = oord;
|
|
372
260
|
destinationIsOord = true;
|
|
373
|
-
} else if (
|
|
374
|
-
destinationId =
|
|
375
|
-
|
|
261
|
+
} else if (region) {
|
|
262
|
+
destinationId = region;
|
|
263
|
+
destinationIsRegion = true;
|
|
264
|
+
} else if (country) {
|
|
265
|
+
destinationId = country;
|
|
266
|
+
destinationIsCountry = true;
|
|
376
267
|
} else if (destinationAirport) {
|
|
377
268
|
destinationCode = destinationAirport;
|
|
378
269
|
destinationIsAirport = true;
|
|
379
270
|
}
|
|
380
271
|
|
|
381
|
-
|
|
272
|
+
return {
|
|
273
|
+
transactionId: currentTransactionId,
|
|
382
274
|
officeId: 1,
|
|
383
|
-
agentId: agentId ?? null,
|
|
275
|
+
agentId: context?.agentId ?? null,
|
|
384
276
|
catalogueId: context!.searchConfiguration.defaultCatalogueId ?? 0,
|
|
385
277
|
searchConfigurationId: context!.searchConfiguration.id,
|
|
386
278
|
language: context!.languageCode ?? 'en-GB',
|
|
387
|
-
|
|
388
|
-
fromDate:
|
|
389
|
-
toDate:
|
|
279
|
+
serviceType: ACCOMMODATION_SERVICE_TYPE,
|
|
280
|
+
fromDate: seed.fromDate,
|
|
281
|
+
toDate: seed.toDate,
|
|
390
282
|
destination: {
|
|
391
283
|
id: Number(destinationId),
|
|
392
284
|
isCountry: destinationIsCountry,
|
|
@@ -396,194 +288,307 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
396
288
|
isAirport: destinationIsAirport,
|
|
397
289
|
code: destinationCode
|
|
398
290
|
} as PackagingDestination,
|
|
399
|
-
productCode:
|
|
400
|
-
rooms:
|
|
291
|
+
productCode: '',
|
|
292
|
+
rooms: getPackagingRequestRoomsFromBookingRooms(seed.rooms),
|
|
401
293
|
tagIds: tagId ? [tagId] : []
|
|
402
294
|
};
|
|
295
|
+
};
|
|
403
296
|
|
|
404
|
-
|
|
297
|
+
const buildSearchSeedFromQueryParams = (params: URLSearchParams): SearchSeed | null => {
|
|
298
|
+
const from = getDateFromParams(params, 'fromDate');
|
|
299
|
+
const to = getDateFromParams(params, 'toDate');
|
|
300
|
+
const rooms = getRoomsFromParams(params, 'rooms');
|
|
301
|
+
const country = getNumberFromParams(params, 'country');
|
|
302
|
+
const region = getNumberFromParams(params, 'region');
|
|
303
|
+
const oord = getNumberFromParams(params, 'oord');
|
|
304
|
+
const city = getNumberFromParams(params, 'location');
|
|
305
|
+
const hotel = getNumberFromParams(params, 'hotel');
|
|
306
|
+
const tagId = getNumberFromParams(params, 'tagId');
|
|
307
|
+
const destinationAirport = getStringFromParams(params, 'destinationAirport');
|
|
308
|
+
const departureAirport = getStringFromParams(params, 'departureAirport');
|
|
405
309
|
|
|
406
|
-
|
|
310
|
+
if (!from || !to) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
fromDate: from,
|
|
316
|
+
toDate: to,
|
|
317
|
+
country,
|
|
318
|
+
region,
|
|
319
|
+
oord,
|
|
320
|
+
location: city,
|
|
321
|
+
hotel,
|
|
322
|
+
hotelCode: hotel ? hotel.toString() : null,
|
|
323
|
+
tagId,
|
|
324
|
+
destinationAirport,
|
|
325
|
+
departureAirport,
|
|
326
|
+
rooms: getRequestRooms(rooms)
|
|
327
|
+
};
|
|
407
328
|
};
|
|
408
329
|
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
var room = { index: 0, travellers: [] } as PackagingRoom;
|
|
413
|
-
range(0, 2).forEach(() => {
|
|
414
|
-
room.travellers.push({
|
|
415
|
-
age: 30
|
|
416
|
-
} as PackagingTraveller);
|
|
417
|
-
});
|
|
418
|
-
return [room];
|
|
419
|
-
}
|
|
330
|
+
const handleConfirmHotelSwap = () => {
|
|
331
|
+
const updatedEntry = swapHotelInPackagingEntry();
|
|
332
|
+
if (!updatedEntry) return;
|
|
420
333
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
334
|
+
dispatch(setEditablePackagingEntry(updatedEntry));
|
|
335
|
+
handleFlyInToggle(false);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const swapHotelInPackagingEntry = (): PackagingEntry | null => {
|
|
339
|
+
const sourceEntry = editablePackagingEntry ?? context?.packagingEntry;
|
|
340
|
+
const details = packagingAccoSearchDetails;
|
|
341
|
+
|
|
342
|
+
if (!sourceEntry || !details?.length) return null;
|
|
343
|
+
|
|
344
|
+
const selectedOptionsPerRoom = getSelectedOptionsPerRoom(details);
|
|
345
|
+
if (!selectedOptionsPerRoom.length) return null;
|
|
346
|
+
|
|
347
|
+
const selectedHotel = details[0];
|
|
348
|
+
|
|
349
|
+
let roomIndex = 0;
|
|
350
|
+
const updatedLines = sourceEntry.lines.map((line) => {
|
|
351
|
+
if (line.serviceType !== ACCOMMODATION_SERVICE_TYPE) {
|
|
352
|
+
return line;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const selectedRoom = selectedOptionsPerRoom.find((x) => x.roomIndex === roomIndex);
|
|
356
|
+
const selectedOption = selectedRoom?.option;
|
|
357
|
+
roomIndex++;
|
|
358
|
+
|
|
359
|
+
if (!selectedOption) {
|
|
360
|
+
return line;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
...line,
|
|
365
|
+
guid: selectedOption.guid,
|
|
366
|
+
productName: selectedHotel.name,
|
|
367
|
+
productCode: selectedHotel.code,
|
|
368
|
+
accommodationName: selectedOption.accommodationName,
|
|
369
|
+
accommodationCode: selectedOption.accommodationCode,
|
|
370
|
+
regimeName: selectedOption.regimeName,
|
|
371
|
+
regimeCode: selectedOption.regimeCode,
|
|
372
|
+
|
|
373
|
+
country: line.country
|
|
374
|
+
? { ...line.country, id: selectedHotel.countryId ?? line.country.id, name: selectedHotel.countryName ?? line.country.name }
|
|
375
|
+
: selectedHotel.countryId
|
|
376
|
+
? { id: selectedHotel.countryId, name: selectedHotel.countryName, localizations: [] }
|
|
377
|
+
: line.country,
|
|
378
|
+
|
|
379
|
+
region: line.region
|
|
380
|
+
? { ...line.region, id: selectedHotel.regionId ?? line.region.id, name: selectedHotel.regionName ?? line.region.name }
|
|
381
|
+
: selectedHotel.regionId
|
|
382
|
+
? { id: selectedHotel.regionId, name: selectedHotel.regionName, localizations: [] }
|
|
383
|
+
: line.region,
|
|
384
|
+
|
|
385
|
+
oord: line.oord
|
|
386
|
+
? { ...line.oord, id: selectedHotel.oordId ?? line.oord.id, name: selectedHotel.oordName ?? line.oord.name }
|
|
387
|
+
: selectedHotel.oordId
|
|
388
|
+
? { id: selectedHotel.oordId, name: selectedHotel.oordName, localizations: [] }
|
|
389
|
+
: line.oord,
|
|
390
|
+
|
|
391
|
+
location: line.location
|
|
392
|
+
? { ...line.location, id: selectedHotel.locationId ?? line.location.id, name: selectedHotel.locationName ?? line.location.name }
|
|
393
|
+
: selectedHotel.locationId
|
|
394
|
+
? { id: selectedHotel.locationId, name: selectedHotel.locationName, localizations: [] }
|
|
395
|
+
: line.location,
|
|
396
|
+
|
|
397
|
+
latitude: selectedHotel.latitude ?? line.latitude,
|
|
398
|
+
longitude: selectedHotel.longitude ?? line.longitude,
|
|
399
|
+
|
|
400
|
+
from: selectedHotel.fromDate ?? line.from,
|
|
401
|
+
to: selectedHotel.toDate ?? line.to,
|
|
402
|
+
isChanged: true
|
|
403
|
+
};
|
|
434
404
|
});
|
|
435
405
|
|
|
436
|
-
return
|
|
406
|
+
return {
|
|
407
|
+
...sourceEntry,
|
|
408
|
+
lines: updatedLines
|
|
409
|
+
};
|
|
437
410
|
};
|
|
438
411
|
|
|
412
|
+
const activeSearchSeed = React.useMemo(() => {
|
|
413
|
+
if (selectedAccommodationSeed) {
|
|
414
|
+
return selectedAccommodationSeed;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (typeof window === 'undefined') return null;
|
|
418
|
+
|
|
419
|
+
const params = new URLSearchParams(window.location.search);
|
|
420
|
+
return buildSearchSeedFromQueryParams(params);
|
|
421
|
+
}, [selectedAccommodationSeed, currentSearch]);
|
|
422
|
+
|
|
439
423
|
useEffect(() => {
|
|
440
424
|
if (typeof document !== 'undefined') {
|
|
441
425
|
document.body.classList.toggle('has-overlay', filtersOpen);
|
|
442
426
|
}
|
|
443
427
|
}, [filtersOpen]);
|
|
444
428
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
429
|
+
const runSearch = async () => {
|
|
430
|
+
try {
|
|
431
|
+
if (!context) return;
|
|
448
432
|
dispatch(setIsLoading(true));
|
|
449
|
-
try {
|
|
450
|
-
if (!context) {
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
433
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
434
|
+
const config: TideClientConfig = {
|
|
435
|
+
host: context.tideConnection.host,
|
|
436
|
+
apiKey: context.tideConnection.apiKey
|
|
437
|
+
};
|
|
458
438
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
439
|
+
const seed = activeSearchSeed;
|
|
440
|
+
if (!seed) {
|
|
441
|
+
throw new Error('Invalid search parameters');
|
|
442
|
+
}
|
|
463
443
|
|
|
464
|
-
|
|
444
|
+
const searchRequest = buildSearchFromSeed(seed);
|
|
445
|
+
const packageSearchResults = await search(config, searchRequest);
|
|
465
446
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const rq = buildSearchFromQueryParams(params);
|
|
473
|
-
if (!rq) {
|
|
474
|
-
throw new Error('Invalid search parameters');
|
|
475
|
-
}
|
|
447
|
+
const enrichedFilters = enrichFiltersWithResults(packageSearchResults, context.filters, context.tags ?? []);
|
|
448
|
+
if (!initialFiltersSet) {
|
|
449
|
+
dispatch(resetFilters(enrichedFilters));
|
|
450
|
+
setInitialFilters(enrichedFilters);
|
|
451
|
+
setInitialFiltersSet(true);
|
|
452
|
+
}
|
|
476
453
|
|
|
477
|
-
|
|
454
|
+
dispatch(setResults(packageSearchResults));
|
|
455
|
+
const initialFilteredResults = applyFilters(packageSearchResults, filters, null);
|
|
456
|
+
dispatch(setFilteredResults(initialFilteredResults));
|
|
457
|
+
|
|
458
|
+
if (packageSearchResults?.length > 0) {
|
|
459
|
+
if (context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
|
|
460
|
+
dispatch(setSelectedSearchResult(packageSearchResults[0]));
|
|
478
461
|
}
|
|
479
|
-
|
|
462
|
+
}
|
|
463
|
+
} catch (err) {
|
|
464
|
+
console.error('Search failed', err);
|
|
465
|
+
} finally {
|
|
466
|
+
dispatch(setIsLoading(false));
|
|
467
|
+
}
|
|
468
|
+
};
|
|
480
469
|
|
|
481
|
-
|
|
470
|
+
const runStartTransaction = async (): Promise<string | null> => {
|
|
471
|
+
try {
|
|
472
|
+
if (!context) return null;
|
|
482
473
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
setInitialFiltersSet(true);
|
|
488
|
-
}
|
|
474
|
+
const config: TideClientConfig = {
|
|
475
|
+
host: context.tideConnection.host,
|
|
476
|
+
apiKey: context.tideConnection.apiKey
|
|
477
|
+
};
|
|
489
478
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
dispatch(setFilteredResults(initialFilteredResults));
|
|
493
|
-
|
|
494
|
-
if (packageSearchResults?.length > 0) {
|
|
495
|
-
if (entryId) {
|
|
496
|
-
const matching = packageSearchResults.find((r) => r.productId === entry?.id);
|
|
497
|
-
|
|
498
|
-
if (matching) {
|
|
499
|
-
dispatch(setSelectedSearchResult(matching));
|
|
500
|
-
}
|
|
501
|
-
} else {
|
|
502
|
-
if (context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
|
|
503
|
-
dispatch(setSelectedSearchResult(packageSearchResults[0]));
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
479
|
+
const transaction = await startTransaction(config);
|
|
480
|
+
console.log('Transaction started', transaction);
|
|
507
481
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
482
|
+
dispatch(setTransactionId(transaction.transactionId));
|
|
483
|
+
|
|
484
|
+
return transaction.transactionId;
|
|
485
|
+
} catch (err) {
|
|
486
|
+
console.error('Transaction failed', err);
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
514
490
|
|
|
515
|
-
|
|
491
|
+
const runHotelSearch = async (currentTransactionId: string, seed: SearchSeed) => {
|
|
492
|
+
try {
|
|
493
|
+
if (!context) return;
|
|
516
494
|
dispatch(setIsLoading(true));
|
|
517
|
-
try {
|
|
518
|
-
if (!context) {
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
495
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
496
|
+
const config: TideClientConfig = {
|
|
497
|
+
host: context.tideConnection.host,
|
|
498
|
+
apiKey: context.tideConnection.apiKey
|
|
499
|
+
};
|
|
526
500
|
|
|
527
|
-
|
|
501
|
+
let searchRequest: PackagingAccommodationRequest = buildPackagingAccommodationRequestFromSeed(seed, currentTransactionId);
|
|
502
|
+
searchRequest.portalId = context.portalId;
|
|
503
|
+
searchRequest.agentId = context.agentId;
|
|
528
504
|
|
|
529
|
-
|
|
505
|
+
const packageAccoSearchResults = await searchPackagingAccommodations(config, searchRequest);
|
|
530
506
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
507
|
+
const enrichedFilters = enrichFiltersWithPackageAccoResults(packageAccoSearchResults, context.filters, context.tags ?? []);
|
|
508
|
+
if (!initialFiltersSet) {
|
|
509
|
+
dispatch(resetFilters(enrichedFilters));
|
|
510
|
+
setInitialFilters(enrichedFilters);
|
|
511
|
+
setInitialFiltersSet(true);
|
|
512
|
+
}
|
|
535
513
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
514
|
+
dispatch(setPackagingAccoResults(packageAccoSearchResults));
|
|
515
|
+
const initialFilteredResults = applyFiltersToPackageAccoResults(packageAccoSearchResults, filters, null);
|
|
516
|
+
dispatch(setFilteredPackagingAccoResults(initialFilteredResults));
|
|
539
517
|
|
|
540
|
-
|
|
518
|
+
dispatch(setIsLoading(false));
|
|
519
|
+
} catch (err) {
|
|
520
|
+
console.error('Search failed', err);
|
|
521
|
+
dispatch(setIsLoading(false));
|
|
522
|
+
}
|
|
523
|
+
};
|
|
541
524
|
|
|
542
|
-
|
|
525
|
+
const runAccommodationFlow = async (seed: SearchSeed) => {
|
|
526
|
+
if (!context || context.showMockup) return;
|
|
543
527
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
}
|
|
528
|
+
let currentTransactionId = context?.packagingEntry?.transactionId || transactionId;
|
|
529
|
+
if (!currentTransactionId) {
|
|
530
|
+
dispatch(setIsLoading(true));
|
|
531
|
+
currentTransactionId = await runStartTransaction();
|
|
532
|
+
}
|
|
550
533
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
534
|
+
if (!currentTransactionId) {
|
|
535
|
+
dispatch(setIsLoading(false));
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
554
538
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
console.error('Search failed', err);
|
|
558
|
-
dispatch(setIsLoading(false));
|
|
559
|
-
}
|
|
560
|
-
};
|
|
539
|
+
await runHotelSearch(currentTransactionId, seed);
|
|
540
|
+
};
|
|
561
541
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
)
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
542
|
+
// separate Search
|
|
543
|
+
useEffect(() => {
|
|
544
|
+
if (
|
|
545
|
+
context?.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
|
|
546
|
+
(context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.searchConfiguration.enableManualPackaging)
|
|
547
|
+
) {
|
|
548
|
+
runSearch();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation) {
|
|
552
|
+
const seed = activeSearchSeed;
|
|
553
|
+
if (seed) {
|
|
554
|
+
runAccommodationFlow(seed);
|
|
571
555
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (
|
|
559
|
+
context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight &&
|
|
560
|
+
context.searchConfiguration.enableManualPackaging &&
|
|
561
|
+
context.searchConfiguration.allowAccommodations &&
|
|
562
|
+
!context?.packagingEntry
|
|
563
|
+
) {
|
|
564
|
+
const seed = activeSearchSeed;
|
|
565
|
+
if (seed) {
|
|
566
|
+
runAccommodationFlow(seed);
|
|
577
567
|
}
|
|
578
568
|
}
|
|
579
|
-
}, [
|
|
569
|
+
}, [
|
|
570
|
+
location.search,
|
|
571
|
+
context?.showMockup,
|
|
572
|
+
context?.searchConfiguration.qsmType,
|
|
573
|
+
context?.searchConfiguration.enableManualPackaging,
|
|
574
|
+
context?.searchConfiguration.allowAccommodations,
|
|
575
|
+
context?.packagingEntry?.transactionId,
|
|
576
|
+
activeSearchSeed
|
|
577
|
+
]);
|
|
580
578
|
|
|
581
|
-
|
|
579
|
+
useEffect(() => {
|
|
580
|
+
if (context?.packagingEntry) {
|
|
581
|
+
console.log('original packaging entry from context', context.packagingEntry);
|
|
582
|
+
dispatch(setEditablePackagingEntry(structuredClone(context.packagingEntry)));
|
|
583
|
+
dispatch(setTransactionId(context.packagingEntry.transactionId));
|
|
584
|
+
}
|
|
585
|
+
}, [context?.packagingEntry]);
|
|
586
|
+
|
|
587
|
+
// separate detailsCall
|
|
582
588
|
useEffect(() => {
|
|
583
589
|
const fetchDetails = async () => {
|
|
584
|
-
setDetailsIsLoading(true);
|
|
585
|
-
console.log('Fetching details for selected search result', selectedSearchResult);
|
|
586
590
|
if (!selectedSearchResult || !context) return;
|
|
591
|
+
setDetailsIsLoading(true);
|
|
587
592
|
if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation || context?.searchConfiguration.qsmType === PortalQsmType.GroupTour) {
|
|
588
593
|
handleFlyInToggle(true);
|
|
589
594
|
}
|
|
@@ -600,15 +605,13 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
600
605
|
return;
|
|
601
606
|
}
|
|
602
607
|
|
|
603
|
-
|
|
604
|
-
let entryId = getStringFromParams(params, 'entryId');
|
|
608
|
+
let requestRooms: BookingPackageRequestRoom[];
|
|
605
609
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
requestRooms = getRequestRoomsFromEntry(entry.rooms);
|
|
610
|
+
if (context?.packagingEntry) {
|
|
611
|
+
requestRooms = getRequestRoomsFromPackagingEntry(context.packagingEntry);
|
|
609
612
|
} else {
|
|
610
|
-
const
|
|
611
|
-
requestRooms = getRequestRooms(
|
|
613
|
+
const seed = activeSearchSeed;
|
|
614
|
+
requestRooms = seed?.rooms?.length ? seed.rooms : getRequestRooms(null);
|
|
612
615
|
}
|
|
613
616
|
|
|
614
617
|
const detailsRequest: BookingPackageRequest<BookingPackageDetailsRequest> = {
|
|
@@ -632,21 +635,9 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
632
635
|
agentId: context.agentId
|
|
633
636
|
};
|
|
634
637
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
console.log('Details:', detailsResponse);
|
|
639
|
-
dispatch(setBookingPackageDetails({ details: detailsResponse?.payload }));
|
|
640
|
-
} else {
|
|
641
|
-
// const detailsWLResponse = await detailsWL(config, detailsRequest);
|
|
642
|
-
// console.log('Details with entryLight:', detailsWLResponse);
|
|
643
|
-
// dispatch(setBookingPackageDetails({ details: detailsWLResponse?.payload?.bookingPackage }));
|
|
644
|
-
// dispatch(setEntry({ entry: detailsWLResponse?.payload?.entry }));
|
|
645
|
-
|
|
646
|
-
const detailsResponse = await details(config, detailsRequest);
|
|
647
|
-
dispatch(setBookingPackageDetails({ details: detailsResponse?.payload }));
|
|
648
|
-
setDetailsIsLoading(false);
|
|
649
|
-
}
|
|
638
|
+
const detailsResponse = await details(config, detailsRequest);
|
|
639
|
+
dispatch(setBookingPackageDetails({ details: detailsResponse?.payload }));
|
|
640
|
+
setDetailsIsLoading(false);
|
|
650
641
|
} catch (err) {
|
|
651
642
|
console.error('Failed to fetch package details', err);
|
|
652
643
|
setDetailsIsLoading(false);
|
|
@@ -655,8 +646,12 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
655
646
|
|
|
656
647
|
const fetchPackagingAccoSearchDetails = async () => {
|
|
657
648
|
if (!selectedPackagingAccoResultCode || !context) return;
|
|
658
|
-
|
|
659
|
-
if (
|
|
649
|
+
setDetailsIsLoading(true);
|
|
650
|
+
if (
|
|
651
|
+
context?.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
|
|
652
|
+
context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
|
|
653
|
+
context?.searchConfiguration.qsmType === PortalQsmType.GroupTour
|
|
654
|
+
) {
|
|
660
655
|
handleFlyInToggle(true);
|
|
661
656
|
}
|
|
662
657
|
|
|
@@ -666,19 +661,15 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
666
661
|
apiKey: context.tideConnection.apiKey
|
|
667
662
|
};
|
|
668
663
|
|
|
669
|
-
console.log('selectedPackagingAccoResultCode', selectedPackagingAccoResultCode);
|
|
670
664
|
const selectedItem = packagingAccoResults.find((r) => r.code === selectedPackagingAccoResultCode);
|
|
671
|
-
console.log('Selected packaging acco item', selectedItem);
|
|
672
665
|
if (!selectedItem) {
|
|
673
666
|
// TODO: handle this case better, show an error message to the user
|
|
674
667
|
return;
|
|
675
668
|
}
|
|
676
669
|
|
|
677
|
-
const
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
let destinationAirport = getStringFromParams(params, 'destinationAirport');
|
|
670
|
+
const seed = activeSearchSeed;
|
|
671
|
+
const tagId = seed?.tagId ?? null;
|
|
672
|
+
const destinationAirport = seed?.destinationAirport ?? null;
|
|
682
673
|
|
|
683
674
|
let destinationId: number | null = null;
|
|
684
675
|
let destinationIsCountry = false;
|
|
@@ -706,6 +697,7 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
706
697
|
}
|
|
707
698
|
|
|
708
699
|
const detailSearchRequest: PackagingAccommodationRequest = {
|
|
700
|
+
transactionId: transactionId ?? '',
|
|
709
701
|
officeId: 1,
|
|
710
702
|
portalId: context.portalId,
|
|
711
703
|
agentId: context.agentId,
|
|
@@ -713,7 +705,7 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
713
705
|
searchConfigurationId: context!.searchConfiguration.id,
|
|
714
706
|
vendorConfigurationId: selectedItem.vendorId,
|
|
715
707
|
language: context!.languageCode ?? 'en-GB',
|
|
716
|
-
serviceType:
|
|
708
|
+
serviceType: ACCOMMODATION_SERVICE_TYPE,
|
|
717
709
|
fromDate: selectedItem.fromDate,
|
|
718
710
|
toDate: selectedItem.toDate,
|
|
719
711
|
destination: {
|
|
@@ -726,15 +718,16 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
726
718
|
code: destinationCode
|
|
727
719
|
} as PackagingDestination,
|
|
728
720
|
productCode: selectedItem.code ? selectedItem.code : '',
|
|
729
|
-
rooms:
|
|
721
|
+
rooms: getPackagingRequestRoomsFromBookingRooms(seed?.rooms ?? null),
|
|
730
722
|
tagIds: tagId ? [tagId] : []
|
|
731
723
|
};
|
|
732
724
|
|
|
733
725
|
const packageAccoSearchDetails = await searchPackagingAccommodations(config, detailSearchRequest);
|
|
734
|
-
console.log('Packaging Acco Search details', packageAccoSearchDetails);
|
|
735
726
|
dispatch(setPackagingAccoSearchDetails(packageAccoSearchDetails));
|
|
727
|
+
setDetailsIsLoading(false);
|
|
736
728
|
} catch (err) {
|
|
737
729
|
console.error('Failed to fetch package details', err);
|
|
730
|
+
setDetailsIsLoading(false);
|
|
738
731
|
}
|
|
739
732
|
};
|
|
740
733
|
|
|
@@ -744,6 +737,7 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
744
737
|
if (selectedPackagingAccoResultCode) {
|
|
745
738
|
fetchPackagingAccoSearchDetails();
|
|
746
739
|
}
|
|
740
|
+
dispatch(setAccommodationFlyInStep('details'));
|
|
747
741
|
}, [selectedSearchResult, selectedPackagingAccoResultCode]);
|
|
748
742
|
|
|
749
743
|
useEffect(() => {
|
|
@@ -756,6 +750,75 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
756
750
|
}
|
|
757
751
|
}, [filters, results, packagingAccoResults, selectedSortType]);
|
|
758
752
|
|
|
753
|
+
useEffect(() => {
|
|
754
|
+
setInitialFiltersSet(false);
|
|
755
|
+
}, [activeSearchSeed]);
|
|
756
|
+
|
|
757
|
+
const handleEditAccommodation = async (segments: PackagingEntryLine[]) => {
|
|
758
|
+
const sourceEntry = editablePackagingEntry ?? context?.packagingEntry;
|
|
759
|
+
if (!sourceEntry) return;
|
|
760
|
+
|
|
761
|
+
const seed = buildSearchSeedFromAccommodationSegments(sourceEntry, segments);
|
|
762
|
+
if (!seed) return;
|
|
763
|
+
|
|
764
|
+
setDetailsIsLoading(true);
|
|
765
|
+
|
|
766
|
+
setSelectedAccommodationSeed(seed);
|
|
767
|
+
dispatch(setAccommodationFlyInStep('results'));
|
|
768
|
+
handleFlyInToggle(true);
|
|
769
|
+
|
|
770
|
+
await runAccommodationFlow(seed);
|
|
771
|
+
setDetailsIsLoading(false);
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
const buildSearchSeedFromAccommodationSegments = (entry: PackagingEntry, segments: PackagingEntryLine[]): SearchSeed | null => {
|
|
775
|
+
if (!segments?.length) return null;
|
|
776
|
+
|
|
777
|
+
const sortedSegments = [...segments].sort((a, b) => new Date(a.from).getTime() - new Date(b.from).getTime());
|
|
778
|
+
|
|
779
|
+
const firstSegment = first(sortedSegments);
|
|
780
|
+
const lastSegment = last(sortedSegments);
|
|
781
|
+
|
|
782
|
+
if (!firstSegment || !lastSegment) return null;
|
|
783
|
+
|
|
784
|
+
return {
|
|
785
|
+
fromDate: toDateOnlyString(firstSegment.from),
|
|
786
|
+
toDate: toDateOnlyString(lastSegment.to),
|
|
787
|
+
country: firstSegment.country?.id ?? null,
|
|
788
|
+
region: firstSegment.region?.id ?? null,
|
|
789
|
+
oord: firstSegment.oord?.id ?? null,
|
|
790
|
+
location: firstSegment.location?.id ?? null,
|
|
791
|
+
hotel: parseHotelId(firstSegment),
|
|
792
|
+
hotelCode: firstSegment.productCode ?? null,
|
|
793
|
+
tagId: null,
|
|
794
|
+
destinationAirport: getDestinationAirportFromEntry(entry.lines ?? []),
|
|
795
|
+
departureAirport: getDepartureAirportFromEntry(entry.lines ?? []),
|
|
796
|
+
rooms: getRequestRoomsFromPackagingSegments(entry, sortedSegments)
|
|
797
|
+
};
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
useEffect(() => {
|
|
801
|
+
const fetchPriceDetails = async () => {
|
|
802
|
+
if (!context || !editablePackagingEntry) return;
|
|
803
|
+
setPricesAreLoading(true);
|
|
804
|
+
try {
|
|
805
|
+
const config: TideClientConfig = {
|
|
806
|
+
host: context.tideConnection.host,
|
|
807
|
+
apiKey: context.tideConnection.apiKey
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
const priceDetails = await getPriceDetails(config, editablePackagingEntry);
|
|
811
|
+
dispatch(setPriceDetails(priceDetails));
|
|
812
|
+
setPricesAreLoading(false);
|
|
813
|
+
} catch (err) {
|
|
814
|
+
console.error('Error fetching price details', err);
|
|
815
|
+
setPricesAreLoading(false);
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
fetchPriceDetails();
|
|
820
|
+
}, [editablePackagingEntry]);
|
|
821
|
+
|
|
759
822
|
return (
|
|
760
823
|
<div id="tide-booking" className="search__bg">
|
|
761
824
|
{context && (
|
|
@@ -790,7 +853,12 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
790
853
|
/>
|
|
791
854
|
)}
|
|
792
855
|
{context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
|
|
793
|
-
<Itinerary
|
|
856
|
+
<Itinerary
|
|
857
|
+
isOpen={itineraryOpen}
|
|
858
|
+
handleSetIsOpen={() => setItineraryOpen(!itineraryOpen)}
|
|
859
|
+
isLoading={pricesAreLoading}
|
|
860
|
+
onEditAccommodation={handleEditAccommodation}
|
|
861
|
+
/>
|
|
794
862
|
)}
|
|
795
863
|
{/* ---------------- Results ---------------- */}
|
|
796
864
|
<div className="search__results">
|
|
@@ -826,7 +894,7 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
826
894
|
)}
|
|
827
895
|
<div className="search__result-row">
|
|
828
896
|
<span className="search__result-row-text">
|
|
829
|
-
{!isLoading && (
|
|
897
|
+
{!isLoading && !context.packagingEntry && (
|
|
830
898
|
<>
|
|
831
899
|
{((context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight &&
|
|
832
900
|
context.searchConfiguration.enableManualPackaging) ||
|
|
@@ -838,7 +906,7 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
838
906
|
</>
|
|
839
907
|
)}
|
|
840
908
|
</span>
|
|
841
|
-
{!isMobile && sortByTypes && sortByTypes.length > 0 && (
|
|
909
|
+
{!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
|
|
842
910
|
<div className="search__result-row-filter">
|
|
843
911
|
<ItemPicker
|
|
844
912
|
items={sortByTypes}
|
|
@@ -855,21 +923,29 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
855
923
|
</div>
|
|
856
924
|
|
|
857
925
|
<div className="search__results__wrapper">
|
|
858
|
-
{context.showTabViews &&
|
|
926
|
+
{context.showTabViews &&
|
|
927
|
+
(context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
|
|
928
|
+
context.searchConfiguration.qsmType === PortalQsmType.Accommodation) && <TabViews />}
|
|
859
929
|
|
|
860
930
|
{context.showRoundTripResults && context.showMockup && <RoundTripResults />}
|
|
861
931
|
|
|
862
932
|
{context.searchConfiguration.qsmType === PortalQsmType.GroupTour && <GroupTourResults isLoading={isLoading} />}
|
|
863
933
|
|
|
864
934
|
{context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight &&
|
|
935
|
+
!context.packagingEntry &&
|
|
865
936
|
context.showFlightResults &&
|
|
866
937
|
bookingPackageDetails?.outwardFlights && <FlightResults flights={bookingPackageDetails?.outwardFlights} isDeparture={true} />}
|
|
867
938
|
|
|
868
|
-
{context.showHotelAccommodationResults && <HotelAccommodationResults isLoading={isLoading} />}
|
|
939
|
+
{context.showHotelAccommodationResults && !context.packagingEntry && <HotelAccommodationResults isLoading={isLoading} />}
|
|
869
940
|
|
|
870
941
|
{context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight &&
|
|
942
|
+
!context.packagingEntry &&
|
|
871
943
|
context.showFlightResults &&
|
|
872
944
|
bookingPackageDetails?.returnFlights && <FlightResults flights={bookingPackageDetails?.returnFlights} isDeparture={false} />}
|
|
945
|
+
|
|
946
|
+
{context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && context.packagingEntry && (
|
|
947
|
+
<span>TODO: Show Full Itinerary here</span>
|
|
948
|
+
)}
|
|
873
949
|
</div>
|
|
874
950
|
</div>
|
|
875
951
|
{/* <button onClick={() => handleFlyInToggle(!flyInIsOpen)}>Toggle FlyIn</button> */}
|
|
@@ -878,8 +954,11 @@ const SearchResultsContainer: React.FC = () => {
|
|
|
878
954
|
srpType={context.searchConfiguration.qsmType}
|
|
879
955
|
isOpen={flyInIsOpen}
|
|
880
956
|
setIsOpen={handleFlyInToggle}
|
|
957
|
+
handleConfirm={() => handleConfirmHotelSwap()}
|
|
881
958
|
onPanelRef={(el) => (panelRef.current = el)}
|
|
882
959
|
detailsLoading={detailsIsLoading}
|
|
960
|
+
accommodationStep={accommodationFlyInStep}
|
|
961
|
+
isPackageEditFlow={!!context.packagingEntry}
|
|
883
962
|
/>
|
|
884
963
|
</>
|
|
885
964
|
)}
|