@qite/tide-booking-component 1.4.97 → 1.4.98
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 +380 -383
- package/build/build-cjs/src/search-results/types.d.ts +2 -1
- package/build/build-esm/index.js +376 -373
- package/build/build-esm/src/search-results/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/search-results/components/itinerary/index.tsx +270 -235
- package/src/search-results/types.ts +2 -1
- package/styles/components/_search.scss +2 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BookingPackage, FlightSearchResponseItem, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
|
|
1
|
+
import { BookingPackage, FlightSearchResponseItem, PackagingEntry, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
export type FlightSelectionMode = 'paired' | 'independent';
|
|
4
4
|
export interface SearchResultsConfiguration {
|
|
@@ -36,6 +36,7 @@ export interface SearchResultsConfiguration {
|
|
|
36
36
|
alt: string;
|
|
37
37
|
};
|
|
38
38
|
onBook?: (result: BookingPackage) => void;
|
|
39
|
+
packagingEntry?: PackagingEntry;
|
|
39
40
|
}
|
|
40
41
|
export type FilterType = 'checkbox' | 'toggle' | 'slider' | 'star-rating';
|
|
41
42
|
export type FilterProperty = 'regime' | 'accommodation' | 'max-duration' | 'price' | 'rating' | 'theme';
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import React, { useContext } from 'react';
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import { differenceInCalendarDays, format } from 'date-fns';
|
|
3
|
+
import { first, isEmpty, last } from 'lodash';
|
|
4
|
+
import { PackagingEntryLine, PackagingEntryLineFlightLine } from '@qite/tide-client';
|
|
5
|
+
|
|
2
6
|
import Icon from '../icon';
|
|
3
7
|
import Spinner from '../spinner/spinner';
|
|
4
|
-
import { SearchResultsRootState } from '../../store/search-results-store';
|
|
5
|
-
import { useSelector } from 'react-redux';
|
|
6
|
-
import { first, isEmpty, last } from 'lodash';
|
|
7
|
-
import { formatPrice, getTranslations } from '../../../shared/utils/localization-util';
|
|
8
|
-
import { differenceInCalendarDays, format } from 'date-fns';
|
|
9
|
-
import { EntryLineLight } from '@qite/tide-client';
|
|
10
8
|
import SearchResultsConfigurationContext from '../../search-results-configuration-context';
|
|
9
|
+
import { formatPrice, getTranslations } from '../../../shared/utils/localization-util';
|
|
11
10
|
|
|
12
11
|
interface ItineraryProps {
|
|
13
12
|
isOpen: boolean;
|
|
@@ -15,295 +14,331 @@ interface ItineraryProps {
|
|
|
15
14
|
isLoading?: boolean;
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const config = first(flight?.metaDatas)?.configuration;
|
|
23
|
-
if (!config) return '';
|
|
24
|
-
|
|
25
|
-
const parsed = JSON.parse(config);
|
|
26
|
-
const line = first(parsed?.FlightLines);
|
|
17
|
+
const FLIGHT_SERVICE_TYPE = 7;
|
|
18
|
+
const ACCOMMODATION_SERVICE_TYPE = 3;
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return '';
|
|
31
|
-
}
|
|
20
|
+
const getFlightLines = (flight?: PackagingEntryLine): PackagingEntryLineFlightLine[] => {
|
|
21
|
+
return flight?.flightInformation?.flightLines ?? [];
|
|
32
22
|
};
|
|
33
23
|
|
|
34
|
-
const
|
|
35
|
-
|
|
24
|
+
const getDepartureTime = (flight?: PackagingEntryLine): string => {
|
|
25
|
+
const firstLine = first(getFlightLines(flight));
|
|
26
|
+
if (!firstLine?.departureTime) return '';
|
|
36
27
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (!config) return '';
|
|
28
|
+
return firstLine.departureTime.slice(0, 5);
|
|
29
|
+
};
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
const getArrivalTime = (flight?: PackagingEntryLine): string => {
|
|
32
|
+
const lastLine = last(getFlightLines(flight));
|
|
33
|
+
if (!lastLine?.arrivalTime) return '';
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
} catch {
|
|
46
|
-
return '';
|
|
47
|
-
}
|
|
35
|
+
return lastLine.arrivalTime.slice(0, 5);
|
|
48
36
|
};
|
|
49
37
|
|
|
50
|
-
const getDuration = (flight?:
|
|
51
|
-
|
|
38
|
+
const getDuration = (flight?: PackagingEntryLine): string => {
|
|
39
|
+
const lines = getFlightLines(flight);
|
|
40
|
+
if (!lines.length) return '';
|
|
41
|
+
|
|
42
|
+
const totalTicks = lines.reduce((sum, line) => sum + (line.durationInTicks ?? 0), 0) || 0;
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
const config = first(flight?.metaDatas)?.configuration;
|
|
55
|
-
if (!config) return '';
|
|
44
|
+
if (!totalTicks) return '';
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
const seconds = totalTicks / 10_000_000;
|
|
47
|
+
const hours = Math.floor(seconds / 3600);
|
|
48
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
59
49
|
|
|
60
|
-
|
|
50
|
+
return `${hours}h ${minutes.toString().padStart(2, '0')}m`;
|
|
51
|
+
};
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
const numberOfNights = (segment: PackagingEntryLine) => {
|
|
54
|
+
return differenceInCalendarDays(new Date(segment.to), new Date(segment.from));
|
|
55
|
+
};
|
|
65
56
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
const getSegmentIcon = (segment: PackagingEntryLine) => {
|
|
58
|
+
switch (segment.serviceType) {
|
|
59
|
+
case 3:
|
|
60
|
+
return <Icon name="ui-bed" width={15} height={15} />;
|
|
61
|
+
case 4:
|
|
62
|
+
return <Icon name="ui-ticket" width={15} height={15} />;
|
|
63
|
+
case 11:
|
|
64
|
+
return <Icon name="ui-ship" width={15} height={15} />;
|
|
65
|
+
case 13:
|
|
66
|
+
case 17:
|
|
67
|
+
case 22:
|
|
68
|
+
return <Icon name="ui-car" width={15} height={15} />;
|
|
69
|
+
default:
|
|
70
|
+
return <Icon name="ui-location" width={15} height={15} />;
|
|
69
71
|
}
|
|
70
72
|
};
|
|
71
73
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
+
const getSegmentTitle = (segment: PackagingEntryLine) => {
|
|
75
|
+
if (segment.accommodationName) return segment.accommodationName;
|
|
76
|
+
return segment.productName;
|
|
74
77
|
};
|
|
75
78
|
|
|
76
79
|
const Itinerary: React.FC<ItineraryProps> = ({ isOpen, handleSetIsOpen, isLoading }) => {
|
|
77
80
|
const context = useContext(SearchResultsConfigurationContext);
|
|
78
81
|
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
79
82
|
|
|
80
|
-
const
|
|
83
|
+
const packagingEntry = context?.packagingEntry;
|
|
84
|
+
|
|
85
|
+
const sortedLines = useMemo(() => {
|
|
86
|
+
return [...(packagingEntry?.lines ?? [])].sort((a, b) => {
|
|
87
|
+
const orderA = a.order ?? Infinity;
|
|
88
|
+
const orderB = b.order ?? Infinity;
|
|
89
|
+
|
|
90
|
+
// First sort by order
|
|
91
|
+
if (orderA !== orderB) {
|
|
92
|
+
return orderA - orderB;
|
|
93
|
+
}
|
|
81
94
|
|
|
82
|
-
|
|
95
|
+
// Fallback to date
|
|
96
|
+
return new Date(a.from).getTime() - new Date(b.from).getTime();
|
|
97
|
+
});
|
|
98
|
+
}, [packagingEntry]);
|
|
99
|
+
|
|
100
|
+
if (!packagingEntry) {
|
|
83
101
|
return null;
|
|
84
102
|
}
|
|
85
103
|
|
|
86
|
-
const firstEntryLine = first(
|
|
87
|
-
const lastEntryLine = last(
|
|
104
|
+
const firstEntryLine = first(sortedLines);
|
|
105
|
+
const lastEntryLine = last(sortedLines);
|
|
106
|
+
|
|
107
|
+
const accommodationLine = sortedLines.find((line) => line.serviceType === ACCOMMODATION_SERVICE_TYPE) ?? firstEntryLine;
|
|
108
|
+
|
|
109
|
+
const country = accommodationLine?.country?.name ?? firstEntryLine?.country?.name;
|
|
110
|
+
const location = accommodationLine?.location?.name ?? accommodationLine?.oord?.name ?? accommodationLine?.region?.name ?? firstEntryLine?.location?.name;
|
|
111
|
+
|
|
112
|
+
const flightSegments = sortedLines.filter((item) => item.serviceType === FLIGHT_SERVICE_TYPE);
|
|
113
|
+
const outboundFlight = first(flightSegments);
|
|
114
|
+
const returnFlight = flightSegments.length > 1 ? last(flightSegments) : undefined;
|
|
115
|
+
|
|
116
|
+
const otherSegments = sortedLines.filter((item) => item.serviceType !== FLIGHT_SERVICE_TYPE);
|
|
88
117
|
|
|
89
|
-
|
|
90
|
-
const location = firstEntryLine?.locationName;
|
|
118
|
+
console.log('other segments', otherSegments);
|
|
91
119
|
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
120
|
+
const numberOfPax = packagingEntry.pax?.length || 1;
|
|
121
|
+
const totalPrice = packagingEntry.price || 0;
|
|
122
|
+
const pricePerPerson = totalPrice / numberOfPax;
|
|
95
123
|
|
|
96
124
|
return (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
<div className="search__filter-row-flex-title">
|
|
109
|
-
<p className="search__filter-small-title">{translations.SRP.ITINERARY_TITLE}</p>
|
|
110
|
-
</div>
|
|
125
|
+
<div className={`search__filters--modal ${isOpen ? 'is-open' : ''}`}>
|
|
126
|
+
<div className="search__filters--background" onClick={handleSetIsOpen}></div>
|
|
127
|
+
|
|
128
|
+
<button className="search__filters--close" onClick={handleSetIsOpen}>
|
|
129
|
+
<Icon name="ui-close" height={24} />
|
|
130
|
+
</button>
|
|
131
|
+
|
|
132
|
+
<div className="search__filters">
|
|
133
|
+
<div className="search__filter-row search__filter__header">
|
|
134
|
+
<div className="search__filter-row-flex-title">
|
|
135
|
+
<p className="search__filter-small-title">{translations.SRP.ITINERARY_TITLE}</p>
|
|
111
136
|
</div>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)}
|
|
125
|
-
<h4 className="search__filter__image__title">{(location || '') + (location && country ? ' - ' : '') + (country || '')}</h4>
|
|
126
|
-
</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<>
|
|
140
|
+
<div className="search__filter-group">
|
|
141
|
+
{context?.destinationImage && (
|
|
142
|
+
<div className="search__filter__image__wrapper">
|
|
143
|
+
<img src={context.destinationImage.url} alt={context.destinationImage.alt} className="search__filter__image" />
|
|
144
|
+
|
|
145
|
+
{packagingEntry?.dossierNumber && (
|
|
146
|
+
<span className="search__filter__image__text">
|
|
147
|
+
{translations.SRP.DOSSIER_NUMBER}: {packagingEntry.dossierNumber}
|
|
148
|
+
</span>
|
|
127
149
|
)}
|
|
150
|
+
|
|
151
|
+
<h4 className="search__filter__image__title">{(location || '') + (location && country ? ' - ' : '') + (country || '')}</h4>
|
|
128
152
|
</div>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div className="search__filter__prices">
|
|
157
|
+
<div className="search__filter__prices__wrapper">
|
|
158
|
+
<h3 className="search__filter__prices--amount">{formatPrice(pricePerPerson, 'EUR')}</h3>
|
|
159
|
+
<p>{translations.SRP.PACKAGE_PRICE_PER_PERSON}</p>
|
|
160
|
+
<p>
|
|
161
|
+
<strong>
|
|
162
|
+
({formatPrice(totalPrice, 'EUR')} {translations.SRP.TOTAL})
|
|
163
|
+
</strong>
|
|
164
|
+
</p>
|
|
165
|
+
</div>
|
|
166
|
+
<button className="cta">{translations.QSM.CONFIRM}</button>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div className="search__filter__itinerary">
|
|
170
|
+
<p>{translations.SRP.DAY_BY_DAY}</p>
|
|
129
171
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
172
|
+
{firstEntryLine && (
|
|
173
|
+
<div className="search__filter__itinerary__country">
|
|
174
|
+
<div className="search__filter__itinerary__country-icon">
|
|
175
|
+
<Icon name="ui-flag" width={17.5} height={20} />
|
|
176
|
+
</div>
|
|
177
|
+
<div className="search__filter__itinerary__country-content">
|
|
136
178
|
<p>
|
|
137
|
-
<strong>
|
|
138
|
-
({formatPrice(entry?.sellingPrice || 0, entry?.currencyCode || 'EUR')} {translations.SRP.TOTAL})
|
|
139
|
-
</strong>
|
|
179
|
+
{format(new Date(firstEntryLine.from), 'EEE. d MMM yyyy')} - <strong>{translations.SRP.START}</strong>
|
|
140
180
|
</p>
|
|
141
181
|
</div>
|
|
142
|
-
<button className="cta">{translations.QSM.CONFIRM}</button>
|
|
143
182
|
</div>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
{outboundFlight && (
|
|
186
|
+
<div className="search__filter__itinerary__transport">
|
|
187
|
+
<div className="search__filter__itinerary__transport-timeline">
|
|
188
|
+
<div className="search__filter__itinerary__transport-timeline-line"></div>
|
|
189
|
+
</div>
|
|
144
190
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
191
|
+
<div className="search__filter__itinerary__transport-item">
|
|
192
|
+
<div className="search__filter__itinerary__transport-date">
|
|
193
|
+
<p className="search__filter__itinerary__transport-date-date">
|
|
194
|
+
<strong>{format(new Date(outboundFlight.from), 'd')}</strong>
|
|
195
|
+
</p>
|
|
196
|
+
<p>{format(new Date(outboundFlight.from), 'MMM')}</p>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div className="search__filter__itinerary__transport-badge">
|
|
200
|
+
<Icon name="ui-plane" height={15} />
|
|
150
201
|
</div>
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
202
|
+
|
|
203
|
+
<div className="search__filter__itinerary__transport-details">
|
|
204
|
+
<h6>{outboundFlight.productName}</h6>
|
|
205
|
+
<p className="search__filter__itinerary__transport-details-plane">
|
|
206
|
+
<span>
|
|
207
|
+
<Icon name="ui-plane-depart" height={14} /> <strong>{getDepartureTime(outboundFlight)}</strong>
|
|
208
|
+
</span>{' '}
|
|
209
|
+
-{' '}
|
|
210
|
+
<span>
|
|
211
|
+
<Icon name="ui-plane-arrive" height={14} /> <strong>{getArrivalTime(outboundFlight)}</strong>
|
|
212
|
+
</span>
|
|
213
|
+
</p>
|
|
214
|
+
<p className="search__filter__itinerary__transport-details-time">
|
|
215
|
+
<span>
|
|
216
|
+
<Icon name="ui-clock" height={20} /> {getDuration(outboundFlight)}
|
|
217
|
+
</span>
|
|
154
218
|
</p>
|
|
155
219
|
</div>
|
|
156
220
|
</div>
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
157
223
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
224
|
+
{!isEmpty(otherSegments) && (
|
|
225
|
+
<div className="search__filter__itinerary__segments">
|
|
226
|
+
{otherSegments.map((segment, index) => (
|
|
227
|
+
<div className="search__filter__itinerary__segments__wrapper" key={`segment-${index}`}>
|
|
228
|
+
<div className="search__filter__itinerary__segments-timeline">
|
|
229
|
+
<div className="search__filter__itinerary__segments-timeline-line"></div>
|
|
162
230
|
</div>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
</
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
</p>
|
|
231
|
+
|
|
232
|
+
<div className="search__filter__itinerary__segment">
|
|
233
|
+
<div className="search__filter__itinerary__segment-item search__filter__itinerary__segment-item--start">
|
|
234
|
+
<div className="search__filter__itinerary__transport-date">
|
|
235
|
+
<p className="search__filter__itinerary__transport-date-date">
|
|
236
|
+
<strong>{format(new Date(segment.from), 'd')}</strong>
|
|
237
|
+
</p>
|
|
238
|
+
<p>{format(new Date(segment.from), 'MMM')}</p>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div className="search__filter__itinerary__segment-badge">
|
|
242
|
+
<Icon name="ui-location" width={15} height={15} />
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<div className="search__filter__itinerary__segment-details">
|
|
246
|
+
<h6>
|
|
247
|
+
{segment.location?.name || segment.oord?.name || segment.region?.name}
|
|
248
|
+
{(segment.location?.name || segment.oord?.name || segment.region?.name) && segment.country?.name ? ', ' : ''}
|
|
249
|
+
{segment.country?.name}
|
|
250
|
+
</h6>
|
|
251
|
+
<p className="search__filter__itinerary__segment-details-text">
|
|
252
|
+
{format(new Date(segment.from), 'EEE. d MMM yyyy')}
|
|
253
|
+
> {format(new Date(segment.to), 'EEE. d MMM yyyy')}
|
|
254
|
+
</p>
|
|
255
|
+
</div>
|
|
189
256
|
</div>
|
|
190
257
|
</div>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
</div>
|
|
200
|
-
<div className="search__filter__itinerary__segment">
|
|
201
|
-
<div className="search__filter__itinerary__segment-item search__filter__itinerary__segment-item--start">
|
|
202
|
-
<div className="search__filter__itinerary__transport-date">
|
|
203
|
-
<p className="search__filter__itinerary__transport-date-date">
|
|
204
|
-
<strong>{format(new Date(segment?.startDate || ''), 'd')}</strong>
|
|
205
|
-
</p>
|
|
206
|
-
<p>{format(new Date(segment?.startDate || ''), 'MMM')}</p>
|
|
207
|
-
</div>
|
|
208
|
-
<div className="search__filter__itinerary__segment-badge">
|
|
209
|
-
<Icon name="ui-location" width={15} height={15} />
|
|
210
|
-
</div>
|
|
211
|
-
<div className="search__filter__itinerary__segment-details">
|
|
212
|
-
<h6>
|
|
213
|
-
{segment?.locationName}, {segment?.countryName}
|
|
214
|
-
</h6>
|
|
215
|
-
<p className="search__filter__itinerary__segment-details-text">
|
|
216
|
-
{format(new Date(segment?.startDate || ''), 'EEE. d MMM yyyy')}
|
|
217
|
-
> {format(new Date(segment?.endDate || ''), 'EEE. d MMM yyyy')}
|
|
258
|
+
|
|
259
|
+
<div className="search__filter__itinerary__segment">
|
|
260
|
+
<div className="search__filter__itinerary__segment-item">
|
|
261
|
+
<div className="search__filter__itinerary__segment-date search__filter__itinerary__segment-nights">
|
|
262
|
+
{segment.serviceType === ACCOMMODATION_SERVICE_TYPE && (
|
|
263
|
+
<>
|
|
264
|
+
<p className="search__filter__itinerary__segment-date-date">
|
|
265
|
+
<strong>{numberOfNights(segment)}</strong>
|
|
218
266
|
</p>
|
|
219
|
-
|
|
220
|
-
|
|
267
|
+
<Icon name="ui-moon" width={16} height={16} />
|
|
268
|
+
</>
|
|
269
|
+
)}
|
|
221
270
|
</div>
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
<div className="search__filter__itinerary__segment-date search__filter__itinerary__segment-nights">
|
|
226
|
-
<p className="search__filter__itinerary__segment-date-date">
|
|
227
|
-
<strong>{numberOfNights(segment)}</strong>
|
|
228
|
-
</p>
|
|
229
|
-
<Icon name="ui-moon" width={16} height={16} />
|
|
230
|
-
</div>
|
|
231
|
-
)}
|
|
232
|
-
<div className="search__filter__itinerary__segment-badge search__filter__itinerary__segment-badge--secondary">
|
|
233
|
-
{segment.productType === 3 && <Icon name="ui-bed" width={15} height={15} />}
|
|
234
|
-
{segment.productType === 4 && <Icon name="ui-ticket" width={15} height={15} />}
|
|
235
|
-
{(segment.productType === 17 || segment.productType === 22) && <Icon name="ui-car" width={15} height={15} />}
|
|
236
|
-
{segment.productType === 11 && <Icon name="ui-ship" width={15} height={15} />}
|
|
237
|
-
</div>
|
|
238
|
-
<div className="search__filter__itinerary__segment-details">
|
|
239
|
-
<h6>{segment?.productName}</h6>
|
|
240
|
-
{/* <div className="rating">
|
|
241
|
-
<Icon name="ui-star" key={`rating-star-1`} width={14} height={14} />
|
|
242
|
-
<Icon name="ui-star" key={`rating-star-2`} width={14} height={14} />
|
|
243
|
-
<Icon name="ui-star" key={`rating-star-3`} width={14} height={14} />
|
|
244
|
-
<Icon name="ui-star" key={`rating-star-4`} width={14} height={14} />
|
|
245
|
-
<Icon name="ui-star" key={`rating-star-5`} width={14} height={14} />
|
|
246
|
-
</div> */}
|
|
247
|
-
</div>
|
|
248
|
-
</div>
|
|
271
|
+
|
|
272
|
+
<div className="search__filter__itinerary__segment-badge search__filter__itinerary__segment-badge--secondary">
|
|
273
|
+
{getSegmentIcon(segment)}
|
|
249
274
|
</div>
|
|
250
|
-
</div>
|
|
251
|
-
))}
|
|
252
|
-
</div>
|
|
253
|
-
)}
|
|
254
275
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
</div>
|
|
260
|
-
<div className="search__filter__itinerary__transport-item">
|
|
261
|
-
<div className="search__filter__itinerary__transport-date">
|
|
262
|
-
<p className="search__filter__itinerary__transport-date-date">
|
|
263
|
-
<strong>{format(new Date(returnFlight?.startDate || ''), 'd')}</strong>
|
|
264
|
-
</p>
|
|
265
|
-
<p>{format(new Date(returnFlight?.startDate || ''), 'MMM')}</p>
|
|
266
|
-
</div>
|
|
267
|
-
<div className="search__filter__itinerary__transport-badge">
|
|
268
|
-
<Icon name="ui-plane" height={15} />
|
|
269
|
-
</div>
|
|
270
|
-
<div className="search__filter__itinerary__transport-details">
|
|
271
|
-
<h6>{returnFlight?.productName}</h6>
|
|
272
|
-
<p className="search__filter__itinerary__transport-details-plane">
|
|
273
|
-
<span>
|
|
274
|
-
<Icon name="ui-plane-depart" height={14} /> <strong>{getDepartureTime(returnFlight)}</strong>
|
|
275
|
-
</span>{' '}
|
|
276
|
-
-{' '}
|
|
277
|
-
<span>
|
|
278
|
-
<Icon name="ui-plane-arrive" height={14} /> <strong>{getArrivalTime(returnFlight)}</strong>
|
|
279
|
-
</span>
|
|
280
|
-
</p>
|
|
281
|
-
<p className="search__filter__itinerary__transport-details-time">
|
|
282
|
-
<span>
|
|
283
|
-
<Icon name="ui-clock" height={20} /> {getDuration(returnFlight)}
|
|
284
|
-
</span>
|
|
285
|
-
</p>
|
|
276
|
+
<div className="search__filter__itinerary__segment-details">
|
|
277
|
+
<h6>{getSegmentTitle(segment)}</h6>
|
|
278
|
+
{segment.regimeName && <p>{segment.regimeName}</p>}
|
|
279
|
+
</div>
|
|
286
280
|
</div>
|
|
287
281
|
</div>
|
|
288
282
|
</div>
|
|
289
|
-
)}
|
|
283
|
+
))}
|
|
284
|
+
</div>
|
|
285
|
+
)}
|
|
286
|
+
|
|
287
|
+
{returnFlight && returnFlight !== outboundFlight && (
|
|
288
|
+
<div className="search__filter__itinerary__transport">
|
|
289
|
+
<div className="search__filter__itinerary__transport-timeline">
|
|
290
|
+
<div className="search__filter__itinerary__transport-timeline-line"></div>
|
|
291
|
+
</div>
|
|
290
292
|
|
|
291
|
-
<div className="
|
|
292
|
-
<div className="
|
|
293
|
-
<
|
|
293
|
+
<div className="search__filter__itinerary__transport-item">
|
|
294
|
+
<div className="search__filter__itinerary__transport-date">
|
|
295
|
+
<p className="search__filter__itinerary__transport-date-date">
|
|
296
|
+
<strong>{format(new Date(returnFlight.from), 'd')}</strong>
|
|
297
|
+
</p>
|
|
298
|
+
<p>{format(new Date(returnFlight.from), 'MMM')}</p>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<div className="search__filter__itinerary__transport-badge">
|
|
302
|
+
<Icon name="ui-plane" height={15} />
|
|
294
303
|
</div>
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
304
|
+
|
|
305
|
+
<div className="search__filter__itinerary__transport-details">
|
|
306
|
+
<h6>{returnFlight.productName}</h6>
|
|
307
|
+
<p className="search__filter__itinerary__transport-details-plane">
|
|
308
|
+
<span>
|
|
309
|
+
<Icon name="ui-plane-depart" height={14} /> <strong>{getDepartureTime(returnFlight)}</strong>
|
|
310
|
+
</span>{' '}
|
|
311
|
+
-{' '}
|
|
312
|
+
<span>
|
|
313
|
+
<Icon name="ui-plane-arrive" height={14} /> <strong>{getArrivalTime(returnFlight)}</strong>
|
|
314
|
+
</span>
|
|
315
|
+
</p>
|
|
316
|
+
<p className="search__filter__itinerary__transport-details-time">
|
|
317
|
+
<span>
|
|
318
|
+
<Icon name="ui-clock" height={20} /> {getDuration(returnFlight)}
|
|
319
|
+
</span>
|
|
298
320
|
</p>
|
|
299
321
|
</div>
|
|
300
322
|
</div>
|
|
301
323
|
</div>
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
324
|
+
)}
|
|
325
|
+
|
|
326
|
+
{lastEntryLine && (
|
|
327
|
+
<div className="search__filter__itinerary__country">
|
|
328
|
+
<div className="search__filter__itinerary__country-icon">
|
|
329
|
+
<Icon name="ui-flag" width={17.5} height={20} />
|
|
330
|
+
</div>
|
|
331
|
+
<div className="search__filter__itinerary__country-content">
|
|
332
|
+
<p>
|
|
333
|
+
{format(new Date(lastEntryLine.to), 'EEE. d MMM yyyy')} - <strong>{translations.SRP.END}</strong>
|
|
334
|
+
</p>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
)}
|
|
338
|
+
</div>
|
|
339
|
+
</>
|
|
305
340
|
</div>
|
|
306
|
-
|
|
341
|
+
</div>
|
|
307
342
|
);
|
|
308
343
|
};
|
|
309
344
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BookingPackage, FlightSearchResponseItem, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
|
|
1
|
+
import { BookingPackage, FlightSearchResponseItem, PackagingEntry, WebsiteConfigurationSearchConfiguration } from '@qite/tide-client';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
export type FlightSelectionMode = 'paired' | 'independent';
|
|
@@ -53,6 +53,7 @@ export interface SearchResultsConfiguration {
|
|
|
53
53
|
|
|
54
54
|
destinationImage?: { url: string; alt: string };
|
|
55
55
|
onBook?: (result: BookingPackage) => void;
|
|
56
|
+
packagingEntry?: PackagingEntry;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
export type FilterType = 'checkbox' | 'toggle' | 'slider' | 'star-rating';
|
|
@@ -493,6 +493,7 @@
|
|
|
493
493
|
}
|
|
494
494
|
|
|
495
495
|
&-date {
|
|
496
|
+
min-width: 26px;
|
|
496
497
|
p {
|
|
497
498
|
margin: 0px;
|
|
498
499
|
color: var(--tide-booking-search-itinerary-filter-transport-date-month-color);
|
|
@@ -670,6 +671,7 @@
|
|
|
670
671
|
display: flex;
|
|
671
672
|
align-items: center;
|
|
672
673
|
flex-flow: row nowrap;
|
|
674
|
+
min-width: 26px;
|
|
673
675
|
max-width: 26px;
|
|
674
676
|
|
|
675
677
|
svg {
|