@qite/tide-booking-component 1.4.34 → 1.4.36
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 +1628 -376
- package/build/build-cjs/qsm/types.d.ts +0 -1
- package/build/build-cjs/search-results/components/filters/filters.d.ts +2 -2
- package/build/build-cjs/search-results/components/flight/flight-results.d.ts +2 -0
- package/build/build-cjs/search-results/components/hotel/hotel-card.d.ts +1 -0
- package/build/build-cjs/search-results/components/itinerary/index.d.ts +2 -2
- package/build/build-cjs/search-results/store/search-results-slice.d.ts +11 -2
- package/build/build-cjs/search-results/types.d.ts +4 -14
- package/build/build-esm/index.js +1628 -376
- package/build/build-esm/qsm/types.d.ts +0 -1
- package/build/build-esm/search-results/components/filters/filters.d.ts +2 -2
- package/build/build-esm/search-results/components/flight/flight-results.d.ts +2 -0
- package/build/build-esm/search-results/components/hotel/hotel-card.d.ts +1 -0
- package/build/build-esm/search-results/components/itinerary/index.d.ts +2 -2
- package/build/build-esm/search-results/store/search-results-slice.d.ts +11 -2
- package/build/build-esm/search-results/types.d.ts +4 -14
- package/package.json +2 -2
- package/src/booking-wizard/features/booking/booking-self-contained.tsx +0 -1
- package/src/booking-wizard/features/travelers-form/controls/gender-control.tsx +5 -5
- package/src/booking-wizard/features/travelers-form/travelers-form.tsx +10 -10
- package/src/content/components/icon.tsx +1 -1
- package/src/content/features/content-page/content-page-self-contained.tsx +0 -1
- package/src/qsm/components/QSMContainer/qsm-container.tsx +15 -13
- package/src/qsm/components/mobile-filter-modal/index.tsx +12 -10
- package/src/qsm/components/travel-class-picker/index.tsx +5 -3
- package/src/qsm/components/travel-input/index.tsx +15 -12
- package/src/qsm/components/travel-input-group/index.tsx +14 -3
- package/src/qsm/components/travel-nationality-picker/index.tsx +5 -3
- package/src/qsm/components/travel-type-picker/index.tsx +5 -3
- package/src/qsm/qsm-configuration-context.ts +0 -1
- package/src/qsm/types.ts +0 -1
- package/src/search-results/components/filters/filters.tsx +15 -16
- package/src/search-results/components/flight/flight-results.tsx +168 -1099
- package/src/search-results/components/hotel/hotel-accommodation-results.tsx +21 -24
- package/src/search-results/components/hotel/hotel-card.tsx +4 -3
- package/src/search-results/components/icon.tsx +1 -1
- package/src/search-results/components/itinerary/index.tsx +233 -131
- package/src/search-results/components/round-trip/round-trip-results.tsx +1 -1
- package/src/search-results/components/search-results-container/search-results-container.tsx +45 -33
- package/src/search-results/components/spinner/spinner.tsx +3 -1
- package/src/search-results/components/tab-views/index.tsx +13 -7
- package/src/search-results/features/flights/flight-search-results-self-contained.tsx +4 -13
- package/src/search-results/features/hotels/hotel-flight-search-results-self-contained.tsx +5 -1
- package/src/search-results/features/hotels/hotel-search-results-self-contained.tsx +4 -13
- package/src/search-results/features/roundtrips/roundtrip-search-results-self-contained.tsx +5 -1
- package/src/search-results/store/search-results-slice.ts +37 -3
- package/src/search-results/types.ts +2 -15
- package/src/shared/translations/ar-SA.json +70 -0
- package/src/shared/translations/da-DK.json +70 -0
- package/src/shared/translations/de-DE.json +70 -0
- package/src/shared/translations/en-GB.json +71 -1
- package/src/shared/translations/es-ES.json +70 -0
- package/src/shared/translations/fr-BE.json +71 -1
- package/src/shared/translations/fr-FR.json +70 -0
- package/src/shared/translations/is-IS.json +72 -2
- package/src/shared/translations/it-IT.json +70 -0
- package/src/shared/translations/ja-JP.json +72 -2
- package/src/shared/translations/nl-BE.json +70 -0
- package/src/shared/translations/nl-NL.json +70 -0
- package/src/shared/translations/no-NO.json +72 -2
- package/src/shared/translations/pl-PL.json +70 -0
- package/src/shared/translations/pt-PT.json +70 -0
- package/src/shared/translations/sv-SE.json +72 -2
- package/styles/components/_search.scss +7 -1
|
@@ -6,16 +6,23 @@ import { useSelector } from 'react-redux';
|
|
|
6
6
|
import { SearchResultsRootState } from '../../store/search-results-store';
|
|
7
7
|
import { BookingPackageItem } from '@qite/tide-client/build/types';
|
|
8
8
|
import { format, parseISO } from 'date-fns';
|
|
9
|
+
import { formatPrice, getTranslations } from '../../../shared/utils/localization-util';
|
|
9
10
|
|
|
10
11
|
interface HotelAccommodationResultsProps {
|
|
11
12
|
isLoading: boolean;
|
|
12
13
|
context: SearchResultsConfiguration;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
const renderResults = (
|
|
16
|
+
const renderResults = (
|
|
17
|
+
results: BookingPackageItem[],
|
|
18
|
+
context: SearchResultsConfiguration,
|
|
19
|
+
cmsMap: Map<any, any>,
|
|
20
|
+
activeTab: string | null,
|
|
21
|
+
translations: any
|
|
22
|
+
) => {
|
|
16
23
|
const renderedResults = results.map((result, index) => {
|
|
17
24
|
const cmsItem = cmsMap.get(result.productId);
|
|
18
|
-
const mappedResult: HotelResult = mapSearchResult(result, cmsItem, context.languageCode);
|
|
25
|
+
const mappedResult: HotelResult = mapSearchResult(result, cmsItem, context.languageCode, translations);
|
|
19
26
|
if (context?.showCustomCards && context?.customCardRenderer) {
|
|
20
27
|
return (
|
|
21
28
|
<div key={`${mappedResult.id}-${index}`} className="search__result-card">
|
|
@@ -23,13 +30,13 @@ const renderResults = (results: BookingPackageItem[], context: SearchResultsConf
|
|
|
23
30
|
</div>
|
|
24
31
|
);
|
|
25
32
|
}
|
|
26
|
-
return <HotelCard key={`${mappedResult.id}-${index}`} result={mappedResult} />;
|
|
33
|
+
return <HotelCard key={`${mappedResult.id}-${index}`} result={mappedResult} translations={translations} />;
|
|
27
34
|
});
|
|
28
35
|
|
|
29
36
|
return <div className={`search__results__cards ${activeTab ? `search__results__cards--${activeTab}` : ''}`}>{renderedResults}</div>;
|
|
30
37
|
};
|
|
31
38
|
|
|
32
|
-
const mapSearchResult = (searchResult: BookingPackageItem, cmsItem: any, languageCode?: string): HotelResult => {
|
|
39
|
+
const mapSearchResult = (searchResult: BookingPackageItem, cmsItem: any, languageCode?: string, translations?: any): HotelResult => {
|
|
33
40
|
return {
|
|
34
41
|
type: 'hotel',
|
|
35
42
|
id: searchResult.productId,
|
|
@@ -38,36 +45,26 @@ const mapSearchResult = (searchResult: BookingPackageItem, cmsItem: any, languag
|
|
|
38
45
|
description: cmsItem?.content?.descriptions?.introductionTitle || '',
|
|
39
46
|
location:
|
|
40
47
|
searchResult.locationName && searchResult.countryName ? `${searchResult.locationName}, ${searchResult.countryName}` : cmsItem?.parentItem?.name || '',
|
|
41
|
-
price: formatPrice(searchResult.
|
|
42
|
-
ctaText:
|
|
43
|
-
days: calculateNights(searchResult.stayFromDate, searchResult.stayToDate),
|
|
48
|
+
price: formatPrice(searchResult.price, searchResult.currencyCode, languageCode),
|
|
49
|
+
ctaText: translations?.SRP.VIEW_DETAILS,
|
|
50
|
+
days: calculateNights(searchResult.stayFromDate, searchResult.stayToDate, translations),
|
|
44
51
|
accommodation: searchResult.accommodationName,
|
|
45
52
|
regime: searchResult.regimeName,
|
|
46
53
|
stars: cmsItem?.content?.general?.stars || searchResult.hotelStars
|
|
47
54
|
};
|
|
48
55
|
};
|
|
49
56
|
|
|
50
|
-
const
|
|
51
|
-
if (!price) {
|
|
52
|
-
return 'Price unavailable';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return new Intl.NumberFormat(languageCode ?? 'en-GB', {
|
|
56
|
-
style: 'currency',
|
|
57
|
-
currency: currencyCode,
|
|
58
|
-
currencyDisplay: 'symbol'
|
|
59
|
-
}).format(price);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const calculateNights = (fromDate: Date, toDate: Date): string => {
|
|
57
|
+
const calculateNights = (fromDate: Date, toDate: Date, translations?: any): string => {
|
|
63
58
|
const from = new Date(fromDate).getTime(); // returns a number
|
|
64
59
|
const to = new Date(toDate).getTime(); // returns a number
|
|
65
60
|
const diffTime = Math.abs(to - from);
|
|
66
61
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
67
|
-
return `${diffDays}
|
|
62
|
+
return `${diffDays} ${translations?.SRP.NIGHTS}`;
|
|
68
63
|
};
|
|
69
64
|
|
|
70
65
|
const HotelAccommodationResults: React.FC<HotelAccommodationResultsProps> = ({ isLoading, context }) => {
|
|
66
|
+
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
67
|
+
|
|
71
68
|
if (isLoading) {
|
|
72
69
|
return <>{context?.customSpinner ?? <Spinner />}</>;
|
|
73
70
|
}
|
|
@@ -75,7 +72,7 @@ const HotelAccommodationResults: React.FC<HotelAccommodationResultsProps> = ({ i
|
|
|
75
72
|
const { results, activeTab } = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
76
73
|
|
|
77
74
|
if (!results.length) {
|
|
78
|
-
return <div className="no-results">{
|
|
75
|
+
return <div className="no-results">{translations.SRP.NO_RESULTS}</div>;
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
const cmsMap = React.useMemo(() => {
|
|
@@ -102,11 +99,11 @@ const HotelAccommodationResults: React.FC<HotelAccommodationResultsProps> = ({ i
|
|
|
102
99
|
</div>
|
|
103
100
|
<div className="search__results__label__text">
|
|
104
101
|
<h3>
|
|
105
|
-
|
|
102
|
+
{translations.SRP.SELECT} <strong>{translations.SRP.ACCOMMODATION}</strong>
|
|
106
103
|
</h3>
|
|
107
104
|
</div>
|
|
108
105
|
</div>
|
|
109
|
-
{renderResults(results, context, cmsMap, activeTab)}
|
|
106
|
+
{renderResults(results, context, cmsMap, activeTab, translations)}
|
|
110
107
|
</>
|
|
111
108
|
);
|
|
112
109
|
};
|
|
@@ -7,9 +7,10 @@ import { setSelectedHotel } from '../../store/search-results-slice';
|
|
|
7
7
|
|
|
8
8
|
interface HotelCardProps {
|
|
9
9
|
result: HotelResult;
|
|
10
|
+
translations?: any;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
const HotelCard: React.FC<HotelCardProps> = ({ result }) => {
|
|
13
|
+
const HotelCard: React.FC<HotelCardProps> = ({ result, translations }) => {
|
|
13
14
|
const dispatch = useDispatch();
|
|
14
15
|
const { selectedHotelId } = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
15
16
|
|
|
@@ -27,7 +28,7 @@ const HotelCard: React.FC<HotelCardProps> = ({ result }) => {
|
|
|
27
28
|
<div className="search__result-card__img-wrapper">
|
|
28
29
|
<img src={result.image} alt={result.title} className="search__result-card__img" />
|
|
29
30
|
<div className="search__result-card__price__wrapper">
|
|
30
|
-
<span className="search__result-card__price__label">
|
|
31
|
+
<span className="search__result-card__price__label">{translations?.SHARED.TOTAL_PRICE}</span>
|
|
31
32
|
<span className="search__result-card__price">{result.price}</span>
|
|
32
33
|
</div>
|
|
33
34
|
</div>
|
|
@@ -75,7 +76,7 @@ const HotelCard: React.FC<HotelCardProps> = ({ result }) => {
|
|
|
75
76
|
{selectedHotelId === result.id ? 'Selected' : 'Select'}
|
|
76
77
|
</button>
|
|
77
78
|
<button type="button" className="cta cta--select" onClick={() => console.log('Clicked on customCard with id:', result.id)}>
|
|
78
|
-
|
|
79
|
+
{translations?.SRP.VIEW_DETAILS}
|
|
79
80
|
</button>
|
|
80
81
|
</div>
|
|
81
82
|
</div>
|
|
@@ -537,7 +537,7 @@ const Icon: React.FC<IconProps> = ({ name, className, title, width, height }) =>
|
|
|
537
537
|
</svg>
|
|
538
538
|
);
|
|
539
539
|
|
|
540
|
-
case 'ui-
|
|
540
|
+
case 'ui-list':
|
|
541
541
|
return (
|
|
542
542
|
<svg
|
|
543
543
|
className={['icon', `icon--${name}`, className].filter((className) => !isEmpty(className)).join(' ')}
|
|
@@ -1,31 +1,112 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
2
|
import Icon from '../icon';
|
|
3
3
|
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
|
+
import SearchResultsConfigurationContext from '../../search-results-configuration-context';
|
|
4
11
|
|
|
5
12
|
interface ItineraryProps {
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
isOpen: boolean;
|
|
14
|
+
handleSetIsOpen: () => void;
|
|
8
15
|
isLoading?: boolean;
|
|
9
16
|
}
|
|
10
17
|
|
|
11
|
-
const
|
|
18
|
+
const getDepartureTime = (flight?: EntryLineLight): string => {
|
|
19
|
+
if (isEmpty(flight?.metaDatas)) return '';
|
|
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);
|
|
27
|
+
|
|
28
|
+
return (line as any)?.DepartureTime ?? '';
|
|
29
|
+
} catch {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getArrivalTime = (flight?: EntryLineLight): string => {
|
|
35
|
+
if (isEmpty(flight?.metaDatas)) return '';
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const config = first(flight?.metaDatas)?.configuration;
|
|
39
|
+
if (!config) return '';
|
|
40
|
+
|
|
41
|
+
const parsed = JSON.parse(config);
|
|
42
|
+
const line = last(parsed?.FlightLines);
|
|
43
|
+
|
|
44
|
+
return (line as any)?.ArrivalTime ?? '';
|
|
45
|
+
} catch {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const getDuration = (flight?: EntryLineLight): string => {
|
|
51
|
+
if (isEmpty(flight?.metaDatas)) return '';
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const config = first(flight?.metaDatas)?.configuration;
|
|
55
|
+
if (!config) return '';
|
|
56
|
+
|
|
57
|
+
const parsed = JSON.parse(config);
|
|
58
|
+
const ticks = parsed?.DurationInTicks;
|
|
59
|
+
|
|
60
|
+
if (!ticks) return '';
|
|
61
|
+
|
|
62
|
+
const seconds = ticks / 10_000_000;
|
|
63
|
+
const hours = Math.floor(seconds / 3600);
|
|
64
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
65
|
+
|
|
66
|
+
return `${hours}h ${minutes.toString().padStart(2, '0')}m`;
|
|
67
|
+
} catch {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const numberOfNights = (segment: EntryLineLight) => {
|
|
73
|
+
return differenceInCalendarDays(new Date(segment.endDate), new Date(segment.startDate));
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const Itinerary: React.FC<ItineraryProps> = ({ isOpen, handleSetIsOpen, isLoading }) => {
|
|
77
|
+
const context = useContext(SearchResultsConfigurationContext);
|
|
78
|
+
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
79
|
+
|
|
80
|
+
const { entry } = useSelector((state: SearchResultsRootState) => state.searchResults);
|
|
81
|
+
|
|
82
|
+
if (isLoading || !entry) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const firstEntryLine = first(entry?.items);
|
|
87
|
+
const lastEntryLine = last(entry?.items);
|
|
88
|
+
|
|
89
|
+
const country = firstEntryLine?.countryName;
|
|
90
|
+
const location = firstEntryLine?.locationName;
|
|
91
|
+
|
|
92
|
+
const outboundFlight = entry?.items.find((item) => item.productType === 7 && !item.isReturnFlight);
|
|
93
|
+
const returnFlight = entry?.items.find((item) => item.productType === 7 && item.isReturnFlight);
|
|
94
|
+
const otherSegments = entry?.items.filter((item) => item.productType !== 7);
|
|
95
|
+
|
|
12
96
|
return (
|
|
13
97
|
<>
|
|
14
98
|
{/* ---------------- Filters ---------------- */}
|
|
15
99
|
|
|
16
100
|
{/* ---------------- Filters desktop ---------------- */}
|
|
17
|
-
<div className={`search__filters--modal ${
|
|
18
|
-
<div className="search__filters--background" onClick={() =>
|
|
19
|
-
<button className="search__filters--close" onClick={() =>
|
|
101
|
+
<div className={`search__filters--modal ${isOpen ? 'is-open' : ''}`}>
|
|
102
|
+
<div className="search__filters--background" onClick={() => handleSetIsOpen()}></div>
|
|
103
|
+
<button className="search__filters--close" onClick={() => handleSetIsOpen()}>
|
|
20
104
|
<Icon name="ui-close" height={24} />
|
|
21
105
|
</button>
|
|
22
106
|
<div className="search__filters">
|
|
23
107
|
<div className="search__filter-row search__filter__header">
|
|
24
108
|
<div className="search__filter-row-flex-title">
|
|
25
|
-
<p className="search__filter-small-title">
|
|
26
|
-
{/* */}
|
|
27
|
-
Your trip summary
|
|
28
|
-
</p>
|
|
109
|
+
<p className="search__filter-small-title">{translations.SRP.ITINERARY_TITLE}</p>
|
|
29
110
|
</div>
|
|
30
111
|
</div>
|
|
31
112
|
{isLoading ? (
|
|
@@ -33,166 +114,187 @@ const Itinerary: React.FC<ItineraryProps> = ({ isMobileFiltersOpen, handleSetIsM
|
|
|
33
114
|
) : (
|
|
34
115
|
<>
|
|
35
116
|
<div className="search__filter-group">
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
src=
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
117
|
+
{context?.destinationImage && (
|
|
118
|
+
<div className="search__filter__image__wrapper">
|
|
119
|
+
<img src={context.destinationImage.url} alt={context.destinationImage.alt} className="search__filter__image" />
|
|
120
|
+
{entry?.number && (
|
|
121
|
+
<span className="search__filter__image__text">
|
|
122
|
+
{translations.SRP.DOSSIER_NUMBER}: {entry?.number}
|
|
123
|
+
</span>
|
|
124
|
+
)}
|
|
125
|
+
<h4 className="search__filter__image__title">{(location || '') + (location && country ? ' - ' : '') + (country || '')}</h4>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
45
128
|
</div>
|
|
46
129
|
|
|
47
130
|
<div className="search__filter__prices">
|
|
48
131
|
<div className="search__filter__prices__wrapper">
|
|
49
|
-
<h3 className="search__filter__prices--amount"
|
|
50
|
-
|
|
132
|
+
<h3 className="search__filter__prices--amount">
|
|
133
|
+
{formatPrice((entry?.sellingPrice || 0) / (entry?.numberOfPax || 1), entry?.currencyCode || 'EUR')}
|
|
134
|
+
</h3>
|
|
135
|
+
<p>{translations.SRP.PACKAGE_PRICE_PER_PERSON}</p>
|
|
51
136
|
<p>
|
|
52
|
-
<strong>
|
|
137
|
+
<strong>
|
|
138
|
+
({formatPrice(entry?.sellingPrice || 0, entry?.currencyCode || 'EUR')} {translations.SRP.TOTAL})
|
|
139
|
+
</strong>
|
|
53
140
|
</p>
|
|
54
141
|
</div>
|
|
55
|
-
<button className="cta">
|
|
142
|
+
<button className="cta">{translations.QSM.CONFIRM}</button>
|
|
56
143
|
</div>
|
|
57
144
|
|
|
58
145
|
<div className="search__filter__itinerary">
|
|
59
|
-
<p>
|
|
146
|
+
<p>{translations.SRP.DAY_BY_DAY}</p>
|
|
60
147
|
<div className="search__filter__itinerary__country">
|
|
61
148
|
<div className="search__filter__itinerary__country-icon">
|
|
62
149
|
<Icon name="ui-flag" width={17.5} height={20} />
|
|
63
150
|
</div>
|
|
64
151
|
<div className="search__filter__itinerary__country-content">
|
|
65
|
-
<h4>Brussels, Belgium</h4>
|
|
66
152
|
<p>
|
|
67
|
-
|
|
153
|
+
{format(new Date(firstEntryLine?.startDate || ''), 'EEE. d MMM yyyy')} - <strong>{translations.SRP.START}</strong>
|
|
68
154
|
</p>
|
|
69
155
|
</div>
|
|
70
156
|
</div>
|
|
71
157
|
|
|
72
|
-
|
|
73
|
-
<div className="search__filter__itinerary__transport
|
|
74
|
-
<div className="search__filter__itinerary__transport-timeline
|
|
75
|
-
|
|
76
|
-
<div className="search__filter__itinerary__transport-item">
|
|
77
|
-
<div className="search__filter__itinerary__transport-date">
|
|
78
|
-
<p className="search__filter__itinerary__transport-date-date">
|
|
79
|
-
<strong>19</strong>
|
|
80
|
-
</p>
|
|
81
|
-
<p>Jan</p>
|
|
82
|
-
</div>
|
|
83
|
-
<div className="search__filter__itinerary__transport-badge">
|
|
84
|
-
<Icon name="ui-plane" height={15} />
|
|
85
|
-
</div>
|
|
86
|
-
<div className="search__filter__itinerary__transport-details">
|
|
87
|
-
<h6>Brussels Airport (BRU) - Tenerife South Airport (TFS)</h6>
|
|
88
|
-
<p className="search__filter__itinerary__transport-details-plane">
|
|
89
|
-
<span>
|
|
90
|
-
<Icon name="ui-plane-depart" height={14} /> <strong>08:00</strong>
|
|
91
|
-
</span>{' '}
|
|
92
|
-
-{' '}
|
|
93
|
-
<span>
|
|
94
|
-
<Icon name="ui-plane-arrive" height={14} /> <strong>12:30</strong>
|
|
95
|
-
</span>
|
|
96
|
-
</p>
|
|
97
|
-
<p className="search__filter__itinerary__transport-details-time">
|
|
98
|
-
<span>
|
|
99
|
-
<Icon name="ui-clock" height={20} /> 4h30m
|
|
100
|
-
</span>
|
|
101
|
-
</p>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<div className="search__filter__itinerary__segments">
|
|
107
|
-
<div className="search__filter__itinerary__segments__wrapper">
|
|
108
|
-
<div className="search__filter__itinerary__segments-timeline">
|
|
109
|
-
<div className="search__filter__itinerary__segments-timeline-line"></div>
|
|
158
|
+
{outboundFlight && (
|
|
159
|
+
<div className="search__filter__itinerary__transport">
|
|
160
|
+
<div className="search__filter__itinerary__transport-timeline">
|
|
161
|
+
<div className="search__filter__itinerary__transport-timeline-line"></div>
|
|
110
162
|
</div>
|
|
111
|
-
<div className="
|
|
112
|
-
<div className="
|
|
113
|
-
<
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
|
|
163
|
+
<div className="search__filter__itinerary__transport-item">
|
|
164
|
+
<div className="search__filter__itinerary__transport-date">
|
|
165
|
+
<p className="search__filter__itinerary__transport-date-date">
|
|
166
|
+
<strong>{format(new Date(outboundFlight?.startDate || ''), 'd')}</strong>
|
|
167
|
+
</p>
|
|
168
|
+
<p>{format(new Date(outboundFlight?.startDate || ''), 'MMM')}</p>
|
|
169
|
+
</div>
|
|
170
|
+
<div className="search__filter__itinerary__transport-badge">
|
|
171
|
+
<Icon name="ui-plane" height={15} />
|
|
172
|
+
</div>
|
|
173
|
+
<div className="search__filter__itinerary__transport-details">
|
|
174
|
+
<h6>{outboundFlight.productName}</h6>
|
|
175
|
+
<p className="search__filter__itinerary__transport-details-plane">
|
|
176
|
+
<span>
|
|
177
|
+
<Icon name="ui-plane-depart" height={14} /> <strong>{getDepartureTime(outboundFlight)}</strong>
|
|
178
|
+
</span>{' '}
|
|
179
|
+
-{' '}
|
|
180
|
+
<span>
|
|
181
|
+
<Icon name="ui-plane-arrive" height={14} /> <strong>{getArrivalTime(outboundFlight)}</strong>
|
|
182
|
+
</span>
|
|
183
|
+
</p>
|
|
184
|
+
<p className="search__filter__itinerary__transport-details-time">
|
|
185
|
+
<span>
|
|
186
|
+
<Icon name="ui-clock" height={20} /> {getDuration(outboundFlight)}
|
|
187
|
+
</span>
|
|
188
|
+
</p>
|
|
126
189
|
</div>
|
|
127
190
|
</div>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
{!isEmpty(otherSegments) && (
|
|
194
|
+
<div className="search__filter__itinerary__segments">
|
|
195
|
+
{otherSegments?.map((segment, index) => (
|
|
196
|
+
<div className="search__filter__itinerary__segments__wrapper" key={`segment-${index}`}>
|
|
197
|
+
<div className="search__filter__itinerary__segments-timeline">
|
|
198
|
+
<div className="search__filter__itinerary__segments-timeline-line"></div>
|
|
135
199
|
</div>
|
|
136
|
-
<div className="search__filter__itinerary__segment
|
|
137
|
-
<
|
|
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')}
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
138
221
|
</div>
|
|
139
|
-
<div className="search__filter__itinerary__segment
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
222
|
+
<div className="search__filter__itinerary__segment">
|
|
223
|
+
<div className="search__filter__itinerary__segment-item">
|
|
224
|
+
{segment.productType === 3 && (
|
|
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>
|
|
147
248
|
</div>
|
|
148
249
|
</div>
|
|
149
250
|
</div>
|
|
150
|
-
|
|
251
|
+
))}
|
|
151
252
|
</div>
|
|
152
|
-
|
|
253
|
+
)}
|
|
153
254
|
|
|
154
|
-
|
|
155
|
-
<div className="search__filter__itinerary__transport
|
|
156
|
-
<div className="search__filter__itinerary__transport-timeline
|
|
157
|
-
|
|
158
|
-
<div className="search__filter__itinerary__transport-item">
|
|
159
|
-
<div className="search__filter__itinerary__transport-date">
|
|
160
|
-
<p className="search__filter__itinerary__transport-date-date">
|
|
161
|
-
<strong>30</strong>
|
|
162
|
-
</p>
|
|
163
|
-
<p>Jan</p>
|
|
164
|
-
</div>
|
|
165
|
-
<div className="search__filter__itinerary__transport-badge">
|
|
166
|
-
<Icon name="ui-plane" height={15} />
|
|
255
|
+
{returnFlight && (
|
|
256
|
+
<div className="search__filter__itinerary__transport">
|
|
257
|
+
<div className="search__filter__itinerary__transport-timeline">
|
|
258
|
+
<div className="search__filter__itinerary__transport-timeline-line"></div>
|
|
167
259
|
</div>
|
|
168
|
-
<div className="search__filter__itinerary__transport-
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
<
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
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>
|
|
286
|
+
</div>
|
|
184
287
|
</div>
|
|
185
288
|
</div>
|
|
186
|
-
|
|
289
|
+
)}
|
|
187
290
|
|
|
188
291
|
<div className="search__filter__itinerary__country">
|
|
189
292
|
<div className="search__filter__itinerary__country-icon">
|
|
190
293
|
<Icon name="ui-flag" width={17.5} height={20} />
|
|
191
294
|
</div>
|
|
192
295
|
<div className="search__filter__itinerary__country-content">
|
|
193
|
-
<h4>Brussels, Belgium</h4>
|
|
194
296
|
<p>
|
|
195
|
-
|
|
297
|
+
{format(new Date(lastEntryLine?.startDate || ''), 'EEE. d MMM yyyy')} - <strong>{translations.SRP.END}</strong>
|
|
196
298
|
</p>
|
|
197
299
|
</div>
|
|
198
300
|
</div>
|
|
@@ -5,7 +5,7 @@ interface RoundTripResultsProps {}
|
|
|
5
5
|
|
|
6
6
|
const RoundTripResults: React.FC<RoundTripResultsProps> = () => {
|
|
7
7
|
return (
|
|
8
|
-
<div className="search__results__cards search__results__cards--
|
|
8
|
+
<div className="search__results__cards search__results__cards--list">
|
|
9
9
|
<div className="search__result-card">
|
|
10
10
|
<div className="search__result-card__allotment">
|
|
11
11
|
<div className="search__result-card__allotment__title__wrapper">
|