@qite/tide-booking-component 1.4.103 → 1.4.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/build-cjs/index.js +2167 -1400
- package/build/build-cjs/src/search-results/components/filters/filters.d.ts +2 -0
- package/build/build-cjs/src/search-results/components/hotel/hotel-accommodation-results.d.ts +1 -0
- package/build/build-cjs/src/search-results/store/search-results-selectors.d.ts +424 -0
- package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +27 -8
- package/build/build-cjs/src/search-results/types.d.ts +14 -2
- package/build/build-cjs/src/search-results/utils/search-results-utils.d.ts +8 -6
- package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +3 -3
- package/build/build-cjs/src/shared/components/flyin/packaging-flights-flyin.d.ts +7 -0
- package/build/build-esm/index.js +2152 -1395
- package/build/build-esm/src/search-results/components/filters/filters.d.ts +2 -0
- package/build/build-esm/src/search-results/components/hotel/hotel-accommodation-results.d.ts +1 -0
- package/build/build-esm/src/search-results/store/search-results-selectors.d.ts +424 -0
- package/build/build-esm/src/search-results/store/search-results-slice.d.ts +27 -8
- package/build/build-esm/src/search-results/types.d.ts +14 -2
- package/build/build-esm/src/search-results/utils/search-results-utils.d.ts +8 -6
- package/build/build-esm/src/shared/components/flyin/flyin.d.ts +3 -3
- package/build/build-esm/src/shared/components/flyin/packaging-flights-flyin.d.ts +7 -0
- package/package.json +1 -1
- package/src/booking-wizard/features/flight-options/index.tsx +6 -2
- package/src/search-results/components/filters/filters.tsx +8 -9
- package/src/search-results/components/hotel/hotel-accommodation-results.tsx +81 -24
- package/src/search-results/components/search-results-container/search-results-container.tsx +118 -102
- package/src/search-results/store/search-results-selectors.ts +73 -0
- package/src/search-results/store/search-results-slice.ts +94 -14
- package/src/search-results/types.ts +14 -2
- package/src/search-results/utils/search-results-utils.ts +310 -58
- package/src/shared/components/flyin/flyin.tsx +102 -19
- package/src/shared/components/flyin/packaging-flights-flyin.tsx +164 -0
- package/styles/components/_flyin.scss +16 -0
- package/styles/components/_search.scss +4 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { BookingPackageItem, PackagingAccommodationResponse } from '@qite/tide-client';
|
|
1
|
+
import { BookingPackageItem, PackagingAccommodationResponse, PackagingFlightResponse } from '@qite/tide-client';
|
|
2
2
|
import { Filter, SortByType, TideTag } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import { filter, flatMap, orderBy } from 'lodash';
|
|
4
|
+
import { getArrivalSegment, getDepartureRangeName, getDepartureSegment, getNumberOfStops } from './flight-utils';
|
|
5
|
+
import { DepartureRange } from '../../shared/types';
|
|
6
|
+
import { durationInTicksInMinutes, rangeFromDateTimeInMinutes } from '../../shared/utils/localization-util';
|
|
4
7
|
|
|
5
8
|
export const enrichFiltersWithResults = (results: BookingPackageItem[], filters: Filter[] | undefined, tags: TideTag[]): Filter[] => {
|
|
6
9
|
if (!results || results.length === 0 || !filters) {
|
|
@@ -79,76 +82,237 @@ export const enrichFiltersWithResults = (results: BookingPackageItem[], filters:
|
|
|
79
82
|
});
|
|
80
83
|
};
|
|
81
84
|
|
|
82
|
-
export const enrichFiltersWithPackageAccoResults = (results: PackagingAccommodationResponse[],
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
export const enrichFiltersWithPackageAccoResults = (results: PackagingAccommodationResponse[], tags: TideTag[]): Filter[] => {
|
|
86
|
+
const filters: Filter[] = [];
|
|
87
|
+
if (!results || results.length === 0) {
|
|
88
|
+
return filters;
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
const regimeFilter: Filter = {
|
|
92
|
+
property: 'regime',
|
|
93
|
+
label: 'Regime',
|
|
94
|
+
type: 'checkbox',
|
|
95
|
+
options: [],
|
|
96
|
+
isFrontendFilter: true
|
|
97
|
+
};
|
|
98
|
+
const map = new Map<string, { name?: string; code: string }>();
|
|
99
|
+
|
|
100
|
+
results.forEach((r) => {
|
|
101
|
+
const rooms = flatMap(r.rooms);
|
|
102
|
+
if (rooms) {
|
|
103
|
+
rooms.map((room) => {
|
|
104
|
+
room.options.map((option) => {
|
|
105
|
+
if (option.regimeCode) {
|
|
106
|
+
map.set(option.regimeCode, {
|
|
107
|
+
name: option.regimeName,
|
|
108
|
+
code: option.regimeCode
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
96
113
|
}
|
|
114
|
+
});
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
116
|
+
regimeFilter.options = Array.from(map.values()).map((regime) => ({
|
|
117
|
+
label: regime.name ?? regime.code,
|
|
118
|
+
value: regime.code,
|
|
119
|
+
isChecked: false
|
|
120
|
+
}));
|
|
121
|
+
filters.push(regimeFilter);
|
|
122
|
+
|
|
123
|
+
const priceFilter: Filter = {
|
|
124
|
+
property: 'price',
|
|
125
|
+
label: 'Prijs',
|
|
126
|
+
type: 'slider',
|
|
127
|
+
isFrontendFilter: true
|
|
128
|
+
};
|
|
129
|
+
const prices = results.map((r) => r.price ?? 0).filter((p) => p > 0);
|
|
130
|
+
priceFilter.min = Math.floor(Math.min(...prices));
|
|
131
|
+
priceFilter.max = Math.ceil(Math.max(...prices));
|
|
132
|
+
|
|
133
|
+
filters.push(priceFilter);
|
|
134
|
+
|
|
135
|
+
return filters;
|
|
136
|
+
};
|
|
100
137
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
138
|
+
export const enrichFiltersWithPackageFlightResults = (results: PackagingFlightResponse[], tags: TideTag[], translations: any): Filter[] => {
|
|
139
|
+
const filters: Filter[] = [];
|
|
140
|
+
if (!results || results.length === 0) {
|
|
141
|
+
return filters;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Airlines
|
|
145
|
+
const airlinesFilter: Filter = {
|
|
146
|
+
label: 'Airlines',
|
|
147
|
+
property: 'airline',
|
|
148
|
+
type: 'checkbox',
|
|
149
|
+
isFrontendFilter: true,
|
|
150
|
+
options: []
|
|
151
|
+
};
|
|
152
|
+
const airlinesFilterMap = new Map<string, { name?: string; code: string }>();
|
|
153
|
+
|
|
154
|
+
results.map((r) => {
|
|
155
|
+
const airlineCode = r.airlineCode;
|
|
156
|
+
const airlineName = r.airlineName;
|
|
157
|
+
if (airlineCode && airlineName) {
|
|
158
|
+
airlinesFilterMap.set(airlineCode, {
|
|
159
|
+
name: airlineName,
|
|
160
|
+
code: airlineCode
|
|
115
161
|
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
116
164
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
165
|
+
airlinesFilter.options = Array.from(airlinesFilterMap.values()).map((airline) => ({
|
|
166
|
+
label: airline.name ?? airline.code,
|
|
167
|
+
value: airline.code,
|
|
168
|
+
isChecked: false
|
|
169
|
+
}));
|
|
170
|
+
|
|
171
|
+
filters.push(airlinesFilter);
|
|
172
|
+
|
|
173
|
+
// Number of stops
|
|
174
|
+
const stopsFilter: Filter = {
|
|
175
|
+
label: 'Number of Stops',
|
|
176
|
+
property: 'numberOfStops',
|
|
177
|
+
type: 'checkbox',
|
|
178
|
+
isFrontendFilter: true,
|
|
179
|
+
options: []
|
|
180
|
+
};
|
|
181
|
+
const stopsMap = new Map<number, { numberOfStops: number }>();
|
|
182
|
+
|
|
183
|
+
results.map((result) => {
|
|
184
|
+
let numberOfStops = getNumberOfStops(result.outward);
|
|
185
|
+
if (!stopsMap.has(numberOfStops)) {
|
|
186
|
+
stopsMap.set(numberOfStops, { numberOfStops });
|
|
122
187
|
}
|
|
188
|
+
});
|
|
123
189
|
|
|
124
|
-
|
|
125
|
-
|
|
190
|
+
stopsFilter.options = Array.from(stopsMap.values()).map((stop) => ({
|
|
191
|
+
label: `${stop.numberOfStops == 0 ? 'direct' : stop.numberOfStops + ` Stop${stop.numberOfStops !== 1 ? 's' : ''}`}`,
|
|
192
|
+
value: stop.numberOfStops,
|
|
193
|
+
isChecked: false
|
|
194
|
+
}));
|
|
195
|
+
|
|
196
|
+
filters.push(stopsFilter);
|
|
197
|
+
|
|
198
|
+
// Departure range
|
|
199
|
+
const departureRangeFilter: Filter = {
|
|
200
|
+
label: 'Departure Range',
|
|
201
|
+
property: 'departureRange',
|
|
202
|
+
type: 'checkbox',
|
|
203
|
+
isFrontendFilter: true,
|
|
204
|
+
options: []
|
|
205
|
+
};
|
|
206
|
+
const departureRangeMap = new Map<DepartureRange, DepartureRange>();
|
|
207
|
+
results.map((result) => {
|
|
208
|
+
const departureRange = rangeFromDateTimeInMinutes(getDepartureSegment(result.outward)?.departureDateTime);
|
|
209
|
+
if (!departureRangeMap.has(departureRange)) {
|
|
210
|
+
departureRangeMap.set(departureRange, departureRange);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
126
213
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
214
|
+
departureRangeFilter.options = orderBy(Array.from(departureRangeMap.values()), ['id'], ['asc']).map((range) => ({
|
|
215
|
+
label: getDepartureRangeName(translations, range),
|
|
216
|
+
value: range,
|
|
217
|
+
isChecked: false
|
|
218
|
+
}));
|
|
219
|
+
|
|
220
|
+
filters.push(departureRangeFilter);
|
|
221
|
+
|
|
222
|
+
// Departure Airport
|
|
223
|
+
const departureAirportFilter: Filter = {
|
|
224
|
+
label: 'Departure Airport',
|
|
225
|
+
property: 'departureAirport',
|
|
226
|
+
type: 'checkbox',
|
|
227
|
+
isFrontendFilter: true,
|
|
228
|
+
options: []
|
|
229
|
+
};
|
|
230
|
+
const departureAirportsMap = new Map<string, { name?: string; code: string }>();
|
|
231
|
+
|
|
232
|
+
results.map((result) => {
|
|
233
|
+
const departureSegment = getDepartureSegment(result.outward);
|
|
234
|
+
const departureAirport = departureSegment?.departureAirportCode;
|
|
235
|
+
if (departureAirport && !departureAirportsMap.has(departureAirport)) {
|
|
236
|
+
departureAirportsMap.set(departureAirport, {
|
|
237
|
+
name: departureSegment?.departureAirportName + ' (' + departureAirport + ')',
|
|
238
|
+
code: departureAirport
|
|
141
239
|
});
|
|
142
|
-
|
|
143
|
-
updatedFilter.options = Array.from(map.values()).map((regime) => ({
|
|
144
|
-
label: regime.name ?? regime.code,
|
|
145
|
-
value: regime.code,
|
|
146
|
-
isChecked: false
|
|
147
|
-
}));
|
|
148
240
|
}
|
|
241
|
+
});
|
|
149
242
|
|
|
150
|
-
|
|
243
|
+
departureAirportFilter.options = Array.from(departureAirportsMap.values()).map((airport) => ({
|
|
244
|
+
label: airport.name ?? airport.code,
|
|
245
|
+
value: airport.code,
|
|
246
|
+
isChecked: false
|
|
247
|
+
}));
|
|
248
|
+
|
|
249
|
+
filters.push(departureAirportFilter);
|
|
250
|
+
|
|
251
|
+
// Arrival Airport
|
|
252
|
+
const arrivalAirportFilter: Filter = {
|
|
253
|
+
label: 'Arrival Airport',
|
|
254
|
+
property: 'arrivalAirport',
|
|
255
|
+
type: 'checkbox',
|
|
256
|
+
isFrontendFilter: true,
|
|
257
|
+
options: []
|
|
258
|
+
};
|
|
259
|
+
const arrivalAirportsMap = new Map<string, { name?: string; code: string }>();
|
|
260
|
+
|
|
261
|
+
results.map((result) => {
|
|
262
|
+
const arrivalSegment = getArrivalSegment(result.outward);
|
|
263
|
+
const arrivalAirport = arrivalSegment?.arrivalAirportCode;
|
|
264
|
+
if (arrivalAirport && !arrivalAirportsMap.has(arrivalAirport)) {
|
|
265
|
+
arrivalAirportsMap.set(arrivalAirport, {
|
|
266
|
+
name: arrivalSegment?.arrivalAirportName + ' (' + arrivalAirport + ')',
|
|
267
|
+
code: arrivalAirport
|
|
268
|
+
});
|
|
269
|
+
}
|
|
151
270
|
});
|
|
271
|
+
|
|
272
|
+
arrivalAirportFilter.options = Array.from(arrivalAirportsMap.values()).map((airport) => ({
|
|
273
|
+
label: airport.name ?? airport.code,
|
|
274
|
+
value: airport.code,
|
|
275
|
+
isChecked: false
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
filters.push(arrivalAirportFilter);
|
|
279
|
+
|
|
280
|
+
// Price
|
|
281
|
+
const priceFilter: Filter = {
|
|
282
|
+
label: 'Price',
|
|
283
|
+
property: 'price',
|
|
284
|
+
type: 'slider',
|
|
285
|
+
isFrontendFilter: true
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const prices = results.map((r) => r.price ?? 0).filter((p) => p > 0);
|
|
289
|
+
if (prices.length > 0) {
|
|
290
|
+
priceFilter.min = Math.floor(Math.min(...prices));
|
|
291
|
+
priceFilter.max = Math.ceil(Math.max(...prices));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
filters.push(priceFilter);
|
|
295
|
+
|
|
296
|
+
// Travel duration
|
|
297
|
+
const travelDurationFilter: Filter = {
|
|
298
|
+
label: 'Travel Duration',
|
|
299
|
+
property: 'travelDuration',
|
|
300
|
+
type: 'slider',
|
|
301
|
+
isFrontendFilter: true
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const minTravelTimeDuration = Math.min(...results.map((result) => result.outward.durationInTicks));
|
|
305
|
+
const maxTravelTimeDuration = Math.max(...results.map((result) => result.outward.durationInTicks));
|
|
306
|
+
const minTravelTimeValue = durationInTicksInMinutes(minTravelTimeDuration);
|
|
307
|
+
const maxTravelTimeValue = durationInTicksInMinutes(maxTravelTimeDuration);
|
|
308
|
+
if (minTravelTimeValue != null && maxTravelTimeValue != null) {
|
|
309
|
+
travelDurationFilter.min = minTravelTimeValue;
|
|
310
|
+
travelDurationFilter.max = maxTravelTimeValue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
filters.push(travelDurationFilter);
|
|
314
|
+
|
|
315
|
+
return filters;
|
|
152
316
|
};
|
|
153
317
|
|
|
154
318
|
export const applyFilters = (results: BookingPackageItem[], filters: Filter[], sortBy: SortByType | null) => {
|
|
@@ -249,3 +413,91 @@ export const applyFiltersToPackageAccoResults = (results: PackagingAccommodation
|
|
|
249
413
|
return 0;
|
|
250
414
|
});
|
|
251
415
|
};
|
|
416
|
+
|
|
417
|
+
export const applyFiltersToPackageFlightResults = (results: PackagingFlightResponse[], filters: Filter[], sortBy: SortByType | null) => {
|
|
418
|
+
const filtered = results.filter((result) => {
|
|
419
|
+
return filters.every((filter) => {
|
|
420
|
+
if (!filter.isFrontendFilter) return true;
|
|
421
|
+
|
|
422
|
+
// Airline
|
|
423
|
+
if (filter.property === 'airline') {
|
|
424
|
+
const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value);
|
|
425
|
+
if (!selected || selected.length === 0) return true;
|
|
426
|
+
return selected.includes(result.airlineCode);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Stops
|
|
430
|
+
if (filter.property === 'numberOfStops') {
|
|
431
|
+
const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value);
|
|
432
|
+
if (!selected || selected.length === 0) return true;
|
|
433
|
+
return selected.includes(getNumberOfStops(result.outward)) && selected.includes(getNumberOfStops(result.return));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Departure range
|
|
437
|
+
if (filter.property === 'departureRange') {
|
|
438
|
+
const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value);
|
|
439
|
+
if (!selected || selected.length === 0) return true;
|
|
440
|
+
return selected.includes(rangeFromDateTimeInMinutes(getDepartureSegment(result.outward)?.departureDateTime));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Departure airport
|
|
444
|
+
if (filter.property === 'departureAirport') {
|
|
445
|
+
const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value);
|
|
446
|
+
if (!selected || selected.length === 0) return true;
|
|
447
|
+
|
|
448
|
+
const departureAirportCode = getDepartureSegment(result.outward)?.departureAirportCode;
|
|
449
|
+
if (!departureAirportCode) return false;
|
|
450
|
+
|
|
451
|
+
return selected.includes(departureAirportCode);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Arrival airport
|
|
455
|
+
if (filter.property === 'arrivalAirport') {
|
|
456
|
+
const selected = filter.options?.filter((o) => o.isChecked).map((o) => o.value);
|
|
457
|
+
if (!selected || selected.length === 0) return true;
|
|
458
|
+
|
|
459
|
+
const arrivalAirportCode = getArrivalSegment(result.outward)?.arrivalAirportCode;
|
|
460
|
+
if (!arrivalAirportCode) return false;
|
|
461
|
+
|
|
462
|
+
return selected.includes(arrivalAirportCode);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// PRICE
|
|
466
|
+
if (filter.property === 'price') {
|
|
467
|
+
if (filter.selectedMin != null && result.price < filter.selectedMin) return false;
|
|
468
|
+
if (filter.selectedMax != null && result.price > filter.selectedMax) return false;
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Travel times
|
|
473
|
+
if (filter.property === 'travelDuration') {
|
|
474
|
+
if (filter.selectedMin != null && durationInTicksInMinutes(result.outward?.durationInTicks) < filter.selectedMin) return false;
|
|
475
|
+
if (filter.selectedMax != null && durationInTicksInMinutes(result.outward?.durationInTicks) > filter.selectedMax) return false;
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return true;
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// SORTING
|
|
484
|
+
if (!sortBy || sortBy.label === 'default') {
|
|
485
|
+
return filtered;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (sortBy.label === 'departureTime') {
|
|
489
|
+
return orderBy(
|
|
490
|
+
results,
|
|
491
|
+
[(result) => getDepartureSegment(result.outward)?.departureDateTime],
|
|
492
|
+
[sortBy.direction] // or "desc"
|
|
493
|
+
);
|
|
494
|
+
} else if (sortBy.label === 'durationInTicks') {
|
|
495
|
+
return orderBy(
|
|
496
|
+
results,
|
|
497
|
+
[(result) => durationInTicksInMinutes(result.outward?.durationInTicks ?? 0)],
|
|
498
|
+
[sortBy.direction] // or "desc"
|
|
499
|
+
);
|
|
500
|
+
} else {
|
|
501
|
+
return orderBy(results, [sortBy.label], [sortBy.direction]);
|
|
502
|
+
}
|
|
503
|
+
};
|
|
@@ -1,23 +1,31 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from 'react';
|
|
1
|
+
import React, { useContext, useEffect, useRef } from 'react';
|
|
2
2
|
import Icon from '../icon';
|
|
3
3
|
import { useFlightSearch } from '../../../search-results/components/flight/flight-search-context';
|
|
4
|
-
import { useDispatch } from 'react-redux';
|
|
4
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
resetFilters,
|
|
7
|
+
setFilters,
|
|
8
|
+
setFlyInType,
|
|
7
9
|
setSelectedFlight,
|
|
8
10
|
setSelectedFlightDetails,
|
|
9
11
|
setSelectedPackagingAccoResult,
|
|
10
|
-
setSelectedSearchResult
|
|
12
|
+
setSelectedSearchResult,
|
|
13
|
+
setSortType
|
|
11
14
|
} from '../../../search-results/store/search-results-slice';
|
|
12
15
|
import FlightsFlyIn from './flights-flyin';
|
|
13
16
|
import AccommodationFlyIn from './accommodation-flyin';
|
|
14
17
|
import { PortalQsmType } from '@qite/tide-client';
|
|
15
18
|
import GroupTourFlyIn from './group-tour-flyin';
|
|
16
|
-
import {
|
|
19
|
+
import { FlyInType, SortByType } from '../../../search-results/types';
|
|
17
20
|
import HotelAccommodationResults from '../../../search-results/components/hotel/hotel-accommodation-results';
|
|
21
|
+
import PackageingFlightsFlyIn from './packaging-flights-flyin';
|
|
22
|
+
import SearchResultsConfigurationContext from '../../../search-results/search-results-configuration-context';
|
|
23
|
+
import { findSortByType, getSortingName, getTranslations } from '../../utils/localization-util';
|
|
24
|
+
import Filters from '../../../search-results/components/filters/filters';
|
|
25
|
+
import { SearchResultsRootState } from '../../../search-results/store/search-results-store';
|
|
26
|
+
import ItemPicker from '../../../search-results/components/item-picker';
|
|
18
27
|
|
|
19
28
|
type FlyInProps = {
|
|
20
|
-
title: string;
|
|
21
29
|
srpType: PortalQsmType;
|
|
22
30
|
isOpen: boolean;
|
|
23
31
|
setIsOpen: (open: boolean) => void;
|
|
@@ -25,23 +33,32 @@ type FlyInProps = {
|
|
|
25
33
|
onPanelRef?: (el: HTMLDivElement | null) => void;
|
|
26
34
|
detailsLoading: boolean;
|
|
27
35
|
handleConfirm?: () => void;
|
|
28
|
-
|
|
36
|
+
flyInType?: FlyInType | null;
|
|
29
37
|
isPackageEditFlow?: boolean;
|
|
38
|
+
sortByTypes?: SortByType[];
|
|
30
39
|
};
|
|
31
40
|
|
|
32
41
|
const FlyIn: React.FC<FlyInProps> = ({
|
|
33
|
-
title,
|
|
34
42
|
srpType,
|
|
35
43
|
isOpen,
|
|
36
44
|
setIsOpen,
|
|
37
45
|
className = '',
|
|
38
46
|
onPanelRef,
|
|
39
47
|
detailsLoading,
|
|
40
|
-
|
|
48
|
+
flyInType,
|
|
41
49
|
isPackageEditFlow,
|
|
42
|
-
handleConfirm
|
|
50
|
+
handleConfirm,
|
|
51
|
+
sortByTypes
|
|
43
52
|
}) => {
|
|
44
53
|
const dispatch = useDispatch();
|
|
54
|
+
const context = useContext(SearchResultsConfigurationContext);
|
|
55
|
+
const language = context?.languageCode ?? 'en-GB';
|
|
56
|
+
const translations = getTranslations(language);
|
|
57
|
+
|
|
58
|
+
const { isLoading, initialFilters, filters, filteredPackagingAccoResults, selectedSortType } = useSelector(
|
|
59
|
+
(state: SearchResultsRootState) => state.searchResults
|
|
60
|
+
);
|
|
61
|
+
|
|
45
62
|
const { onCancelSearch } = useFlightSearch();
|
|
46
63
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
47
64
|
|
|
@@ -81,26 +98,52 @@ const FlyIn: React.FC<FlyInProps> = ({
|
|
|
81
98
|
dispatch(setSelectedSearchResult(null));
|
|
82
99
|
dispatch(setSelectedPackagingAccoResult(null));
|
|
83
100
|
}
|
|
84
|
-
dispatch(
|
|
101
|
+
dispatch(setFlyInType('acco-details'));
|
|
85
102
|
setIsOpen(false);
|
|
86
103
|
}
|
|
87
104
|
};
|
|
88
105
|
|
|
89
106
|
const handleGoBack = () => {
|
|
90
|
-
dispatch(
|
|
107
|
+
dispatch(setFlyInType('acco-results'));
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const handleSortChange = (newSortKey: string, direction?: string) => {
|
|
111
|
+
if (sortByTypes === undefined) return;
|
|
112
|
+
const newSortByType = findSortByType(sortByTypes, newSortKey, direction ?? 'asc');
|
|
113
|
+
if (newSortByType) {
|
|
114
|
+
dispatch(setSortType(newSortByType));
|
|
115
|
+
}
|
|
91
116
|
};
|
|
92
117
|
|
|
93
118
|
return (
|
|
94
|
-
<div
|
|
119
|
+
<div
|
|
120
|
+
className={`flyin ${isOpen ? 'flyin--active' : ''} ${className} ${
|
|
121
|
+
isPackageEditFlow || flyInType === 'flight-outward-results' || flyInType === 'flight-return-results' || flyInType === 'acco-results'
|
|
122
|
+
? 'flyin--large'
|
|
123
|
+
: ''
|
|
124
|
+
}`}>
|
|
95
125
|
<div className={`flyin__panel ${isOpen ? 'flyin__panel--active' : ''}`} ref={panelRef}>
|
|
96
126
|
<div className="flyin__content">
|
|
97
127
|
<div className="flyin__content-title-row">
|
|
98
|
-
<h3 className="flyin__content-title">
|
|
128
|
+
<h3 className="flyin__content-title">
|
|
129
|
+
{srpType === PortalQsmType.Flight && 'Select your fare'}
|
|
130
|
+
{srpType === PortalQsmType.Accommodation ||
|
|
131
|
+
(srpType === PortalQsmType.AccommodationAndFlight &&
|
|
132
|
+
(flyInType === 'acco-results' || flyInType === 'acco-details') &&
|
|
133
|
+
`${translations.SRP.SELECT} ${translations.SRP.ACCOMMODATION}`)}
|
|
134
|
+
{srpType === PortalQsmType.AccommodationAndFlight &&
|
|
135
|
+
flyInType === 'flight-outward-results' &&
|
|
136
|
+
`${translations.SRP.SELECT} ${translations.FLIGHTS_FORM.OUTWARD_FLIGHT}`}
|
|
137
|
+
{srpType === PortalQsmType.AccommodationAndFlight &&
|
|
138
|
+
flyInType === 'flight-return-results' &&
|
|
139
|
+
`${translations.SRP.SELECT} ${translations.FLIGHTS_FORM.RETURN_FLIGHT}`}
|
|
140
|
+
{srpType === PortalQsmType.AccommodationAndFlight && flyInType === 'flight-details' && 'Select your fare'}
|
|
141
|
+
</h3>
|
|
99
142
|
<span className="flyin__close" onClick={() => handleClose()}>
|
|
100
143
|
<Icon name="ui-close" width={30} height={30} aria-hidden="true" />
|
|
101
144
|
</span>
|
|
102
145
|
</div>
|
|
103
|
-
{isPackageEditFlow &&
|
|
146
|
+
{isPackageEditFlow && flyInType === 'acco-details' && (
|
|
104
147
|
<div className="flyin__content-title-row">
|
|
105
148
|
<div onClick={handleGoBack} className="flyin__content-title__back">
|
|
106
149
|
<Icon name="ui-chevron" width={14} height={14} aria-hidden="true" />
|
|
@@ -111,16 +154,56 @@ const FlyIn: React.FC<FlyInProps> = ({
|
|
|
111
154
|
</div>
|
|
112
155
|
{srpType === PortalQsmType.Flight && <FlightsFlyIn isOpen={isOpen} setIsOpen={setIsOpen} />}
|
|
113
156
|
|
|
114
|
-
{(srpType === PortalQsmType.Accommodation || srpType === PortalQsmType.AccommodationAndFlight) &&
|
|
115
|
-
<div className="flyin__content">
|
|
116
|
-
<
|
|
157
|
+
{(srpType === PortalQsmType.Accommodation || srpType === PortalQsmType.AccommodationAndFlight) && flyInType === 'acco-results' && (
|
|
158
|
+
<div className="flyin__content flyin__content--columns">
|
|
159
|
+
<Filters
|
|
160
|
+
initialFilters={initialFilters}
|
|
161
|
+
filters={filters}
|
|
162
|
+
isOpen={false}
|
|
163
|
+
handleSetIsOpen={() => {}}
|
|
164
|
+
// handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
|
|
165
|
+
isLoading={isLoading}
|
|
166
|
+
setFilters={(filters) => dispatch(setFilters(filters))}
|
|
167
|
+
resetFilters={(filters) => dispatch(resetFilters(filters))}
|
|
168
|
+
/>
|
|
169
|
+
<div className="search__results__wrapper">
|
|
170
|
+
<div className="search__result-row">
|
|
171
|
+
<span className="search__result-row-text">
|
|
172
|
+
{!isLoading && (
|
|
173
|
+
<>
|
|
174
|
+
{filteredPackagingAccoResults?.length && filteredPackagingAccoResults.length}
|
|
175
|
+
{translations.SRP.TOTAL_RESULTS_LABEL}
|
|
176
|
+
</>
|
|
177
|
+
)}
|
|
178
|
+
</span>
|
|
179
|
+
{sortByTypes && sortByTypes.length > 0 && (
|
|
180
|
+
<div className="search__result-row-filter">
|
|
181
|
+
<ItemPicker
|
|
182
|
+
items={sortByTypes}
|
|
183
|
+
selection={selectedSortType?.label || undefined}
|
|
184
|
+
selectedSortByType={selectedSortType}
|
|
185
|
+
label={translations.SRP.SORTBY}
|
|
186
|
+
placeholder={translations.SRP.SORTBY}
|
|
187
|
+
classModifier="travel-class-picker__items"
|
|
188
|
+
valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
|
|
189
|
+
onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
<HotelAccommodationResults isLoading={detailsLoading} isFlyIn={true} />
|
|
195
|
+
</div>
|
|
117
196
|
</div>
|
|
118
197
|
)}
|
|
119
198
|
|
|
120
|
-
{(srpType === PortalQsmType.Accommodation || srpType === PortalQsmType.AccommodationAndFlight) &&
|
|
199
|
+
{(srpType === PortalQsmType.Accommodation || srpType === PortalQsmType.AccommodationAndFlight) && flyInType === 'acco-details' && (
|
|
121
200
|
<AccommodationFlyIn isLoading={detailsLoading} handleConfirm={handleConfirm!} />
|
|
122
201
|
)}
|
|
123
202
|
|
|
203
|
+
{srpType === PortalQsmType.AccommodationAndFlight && (flyInType === 'flight-outward-results' || flyInType === 'flight-return-results') && (
|
|
204
|
+
<PackageingFlightsFlyIn isOpen={isOpen} setIsOpen={setIsOpen} />
|
|
205
|
+
)}
|
|
206
|
+
|
|
124
207
|
{srpType === PortalQsmType.GroupTour && <GroupTourFlyIn isLoading={detailsLoading} isOpen={isOpen} setIsOpen={setIsOpen} />}
|
|
125
208
|
</div>
|
|
126
209
|
</div>
|