@qite/tide-booking-component 1.4.34 → 1.4.35
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 +1627 -374
- 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 +0 -14
- package/build/build-esm/index.js +1627 -374
- 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 +0 -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 +229 -129
- 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 +1 -14
- package/src/search-results/features/hotels/hotel-search-results-self-contained.tsx +1 -14
- package/src/search-results/store/search-results-slice.ts +37 -3
- package/src/search-results/types.ts +0 -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 ? (
|
|
@@ -34,165 +115,184 @@ const Itinerary: React.FC<ItineraryProps> = ({ isMobileFiltersOpen, handleSetIsM
|
|
|
34
115
|
<>
|
|
35
116
|
<div className="search__filter-group">
|
|
36
117
|
<div className="search__filter__image__wrapper">
|
|
37
|
-
<img
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<h4 className="search__filter__image__title">
|
|
118
|
+
<img src="" alt="" className="search__filter__image" />
|
|
119
|
+
{entry?.number && (
|
|
120
|
+
<span className="search__filter__image__text">
|
|
121
|
+
{translations.SRP.DOSSIER_NUMBER}: {entry?.number}
|
|
122
|
+
</span>
|
|
123
|
+
)}
|
|
124
|
+
<h4 className="search__filter__image__title">{(location || '') + (location && country ? ' - ' : '') + (country || '')}</h4>
|
|
44
125
|
</div>
|
|
45
126
|
</div>
|
|
46
127
|
|
|
47
128
|
<div className="search__filter__prices">
|
|
48
129
|
<div className="search__filter__prices__wrapper">
|
|
49
|
-
<h3 className="search__filter__prices--amount"
|
|
50
|
-
|
|
130
|
+
<h3 className="search__filter__prices--amount">
|
|
131
|
+
{formatPrice((entry?.sellingPrice || 0) / (entry?.numberOfPax || 1), entry?.currencyCode || 'EUR')}
|
|
132
|
+
</h3>
|
|
133
|
+
<p>{translations.SRP.PACKAGE_PRICE_PER_PERSON}</p>
|
|
51
134
|
<p>
|
|
52
|
-
<strong>
|
|
135
|
+
<strong>
|
|
136
|
+
({formatPrice(entry?.sellingPrice || 0, entry?.currencyCode || 'EUR')} {translations.SRP.TOTAL})
|
|
137
|
+
</strong>
|
|
53
138
|
</p>
|
|
54
139
|
</div>
|
|
55
|
-
<button className="cta">
|
|
140
|
+
<button className="cta">{translations.QSM.CONFIRM}</button>
|
|
56
141
|
</div>
|
|
57
142
|
|
|
58
143
|
<div className="search__filter__itinerary">
|
|
59
|
-
<p>
|
|
144
|
+
<p>{translations.SRP.DAY_BY_DAY}</p>
|
|
60
145
|
<div className="search__filter__itinerary__country">
|
|
61
146
|
<div className="search__filter__itinerary__country-icon">
|
|
62
147
|
<Icon name="ui-flag" width={17.5} height={20} />
|
|
63
148
|
</div>
|
|
64
149
|
<div className="search__filter__itinerary__country-content">
|
|
65
|
-
<h4>Brussels, Belgium</h4>
|
|
66
150
|
<p>
|
|
67
|
-
|
|
151
|
+
{format(new Date(firstEntryLine?.startDate || ''), 'EEE. d MMM yyyy')} - <strong>{translations.SRP.START}</strong>
|
|
68
152
|
</p>
|
|
69
153
|
</div>
|
|
70
154
|
</div>
|
|
71
155
|
|
|
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>
|
|
156
|
+
{outboundFlight && (
|
|
157
|
+
<div className="search__filter__itinerary__transport">
|
|
158
|
+
<div className="search__filter__itinerary__transport-timeline">
|
|
159
|
+
<div className="search__filter__itinerary__transport-timeline-line"></div>
|
|
110
160
|
</div>
|
|
111
|
-
<div className="
|
|
112
|
-
<div className="
|
|
113
|
-
<
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
|
|
161
|
+
<div className="search__filter__itinerary__transport-item">
|
|
162
|
+
<div className="search__filter__itinerary__transport-date">
|
|
163
|
+
<p className="search__filter__itinerary__transport-date-date">
|
|
164
|
+
<strong>{format(new Date(outboundFlight?.startDate || ''), 'd')}</strong>
|
|
165
|
+
</p>
|
|
166
|
+
<p>{format(new Date(outboundFlight?.startDate || ''), 'MMM')}</p>
|
|
167
|
+
</div>
|
|
168
|
+
<div className="search__filter__itinerary__transport-badge">
|
|
169
|
+
<Icon name="ui-plane" height={15} />
|
|
170
|
+
</div>
|
|
171
|
+
<div className="search__filter__itinerary__transport-details">
|
|
172
|
+
<h6>{outboundFlight.productName}</h6>
|
|
173
|
+
<p className="search__filter__itinerary__transport-details-plane">
|
|
174
|
+
<span>
|
|
175
|
+
<Icon name="ui-plane-depart" height={14} /> <strong>{getDepartureTime(outboundFlight)}</strong>
|
|
176
|
+
</span>{' '}
|
|
177
|
+
-{' '}
|
|
178
|
+
<span>
|
|
179
|
+
<Icon name="ui-plane-arrive" height={14} /> <strong>{getArrivalTime(outboundFlight)}</strong>
|
|
180
|
+
</span>
|
|
181
|
+
</p>
|
|
182
|
+
<p className="search__filter__itinerary__transport-details-time">
|
|
183
|
+
<span>
|
|
184
|
+
<Icon name="ui-clock" height={20} /> {getDuration(outboundFlight)}
|
|
185
|
+
</span>
|
|
186
|
+
</p>
|
|
126
187
|
</div>
|
|
127
188
|
</div>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
189
|
+
</div>
|
|
190
|
+
)}
|
|
191
|
+
{!isEmpty(otherSegments) && (
|
|
192
|
+
<div className="search__filter__itinerary__segments">
|
|
193
|
+
{otherSegments?.map((segment, index) => (
|
|
194
|
+
<div className="search__filter__itinerary__segments__wrapper" key={`segment-${index}`}>
|
|
195
|
+
<div className="search__filter__itinerary__segments-timeline">
|
|
196
|
+
<div className="search__filter__itinerary__segments-timeline-line"></div>
|
|
135
197
|
</div>
|
|
136
|
-
<div className="search__filter__itinerary__segment
|
|
137
|
-
<
|
|
198
|
+
<div className="search__filter__itinerary__segment">
|
|
199
|
+
<div className="search__filter__itinerary__segment-item search__filter__itinerary__segment-item--start">
|
|
200
|
+
<div className="search__filter__itinerary__transport-date">
|
|
201
|
+
<p className="search__filter__itinerary__transport-date-date">
|
|
202
|
+
<strong>{format(new Date(segment?.startDate || ''), 'd')}</strong>
|
|
203
|
+
</p>
|
|
204
|
+
<p>{format(new Date(segment?.startDate || ''), 'MMM')}</p>
|
|
205
|
+
</div>
|
|
206
|
+
<div className="search__filter__itinerary__segment-badge">
|
|
207
|
+
<Icon name="ui-location" width={15} height={15} />
|
|
208
|
+
</div>
|
|
209
|
+
<div className="search__filter__itinerary__segment-details">
|
|
210
|
+
<h6>
|
|
211
|
+
{segment?.locationName}, {segment?.countryName}
|
|
212
|
+
</h6>
|
|
213
|
+
<p className="search__filter__itinerary__segment-details-text">
|
|
214
|
+
{format(new Date(segment?.startDate || ''), 'EEE. d MMM yyyy')}
|
|
215
|
+
> {format(new Date(segment?.endDate || ''), 'EEE. d MMM yyyy')}
|
|
216
|
+
</p>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
138
219
|
</div>
|
|
139
|
-
<div className="search__filter__itinerary__segment
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
220
|
+
<div className="search__filter__itinerary__segment">
|
|
221
|
+
<div className="search__filter__itinerary__segment-item">
|
|
222
|
+
{segment.productType === 3 && (
|
|
223
|
+
<div className="search__filter__itinerary__segment-date search__filter__itinerary__segment-nights">
|
|
224
|
+
<p className="search__filter__itinerary__segment-date-date">
|
|
225
|
+
<strong>{numberOfNights(segment)}</strong>
|
|
226
|
+
</p>
|
|
227
|
+
<Icon name="ui-moon" width={16} height={16} />
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
<div className="search__filter__itinerary__segment-badge search__filter__itinerary__segment-badge--secondary">
|
|
231
|
+
{segment.productType === 3 && <Icon name="ui-bed" width={15} height={15} />}
|
|
232
|
+
{segment.productType === 4 && <Icon name="ui-ticket" width={15} height={15} />}
|
|
233
|
+
{(segment.productType === 17 || segment.productType === 22) && <Icon name="ui-car" width={15} height={15} />}
|
|
234
|
+
{segment.productType === 11 && <Icon name="ui-ship" width={15} height={15} />}
|
|
235
|
+
</div>
|
|
236
|
+
<div className="search__filter__itinerary__segment-details">
|
|
237
|
+
<h6>{segment?.productName}</h6>
|
|
238
|
+
{/* <div className="rating">
|
|
239
|
+
<Icon name="ui-star" key={`rating-star-1`} width={14} height={14} />
|
|
240
|
+
<Icon name="ui-star" key={`rating-star-2`} width={14} height={14} />
|
|
241
|
+
<Icon name="ui-star" key={`rating-star-3`} width={14} height={14} />
|
|
242
|
+
<Icon name="ui-star" key={`rating-star-4`} width={14} height={14} />
|
|
243
|
+
<Icon name="ui-star" key={`rating-star-5`} width={14} height={14} />
|
|
244
|
+
</div> */}
|
|
245
|
+
</div>
|
|
147
246
|
</div>
|
|
148
247
|
</div>
|
|
149
248
|
</div>
|
|
150
|
-
|
|
249
|
+
))}
|
|
151
250
|
</div>
|
|
152
|
-
|
|
251
|
+
)}
|
|
153
252
|
|
|
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>
|
|
253
|
+
{returnFlight && (
|
|
254
|
+
<div className="search__filter__itinerary__transport">
|
|
255
|
+
<div className="search__filter__itinerary__transport-timeline">
|
|
256
|
+
<div className="search__filter__itinerary__transport-timeline-line"></div>
|
|
164
257
|
</div>
|
|
165
|
-
<div className="search__filter__itinerary__transport-
|
|
166
|
-
<
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
258
|
+
<div className="search__filter__itinerary__transport-item">
|
|
259
|
+
<div className="search__filter__itinerary__transport-date">
|
|
260
|
+
<p className="search__filter__itinerary__transport-date-date">
|
|
261
|
+
<strong>{format(new Date(returnFlight?.startDate || ''), 'd')}</strong>
|
|
262
|
+
</p>
|
|
263
|
+
<p>{format(new Date(returnFlight?.startDate || ''), 'MMM')}</p>
|
|
264
|
+
</div>
|
|
265
|
+
<div className="search__filter__itinerary__transport-badge">
|
|
266
|
+
<Icon name="ui-plane" height={15} />
|
|
267
|
+
</div>
|
|
268
|
+
<div className="search__filter__itinerary__transport-details">
|
|
269
|
+
<h6>{returnFlight?.productName}</h6>
|
|
270
|
+
<p className="search__filter__itinerary__transport-details-plane">
|
|
271
|
+
<span>
|
|
272
|
+
<Icon name="ui-plane-depart" height={14} /> <strong>{getDepartureTime(returnFlight)}</strong>
|
|
273
|
+
</span>{' '}
|
|
274
|
+
-{' '}
|
|
275
|
+
<span>
|
|
276
|
+
<Icon name="ui-plane-arrive" height={14} /> <strong>{getArrivalTime(returnFlight)}</strong>
|
|
277
|
+
</span>
|
|
278
|
+
</p>
|
|
279
|
+
<p className="search__filter__itinerary__transport-details-time">
|
|
280
|
+
<span>
|
|
281
|
+
<Icon name="ui-clock" height={20} /> {getDuration(returnFlight)}
|
|
282
|
+
</span>
|
|
283
|
+
</p>
|
|
284
|
+
</div>
|
|
184
285
|
</div>
|
|
185
286
|
</div>
|
|
186
|
-
|
|
287
|
+
)}
|
|
187
288
|
|
|
188
289
|
<div className="search__filter__itinerary__country">
|
|
189
290
|
<div className="search__filter__itinerary__country-icon">
|
|
190
291
|
<Icon name="ui-flag" width={17.5} height={20} />
|
|
191
292
|
</div>
|
|
192
293
|
<div className="search__filter__itinerary__country-content">
|
|
193
|
-
<h4>Brussels, Belgium</h4>
|
|
194
294
|
<p>
|
|
195
|
-
|
|
295
|
+
{format(new Date(lastEntryLine?.startDate || ''), 'EEE. d MMM yyyy')} - <strong>{translations.SRP.END}</strong>
|
|
196
296
|
</p>
|
|
197
297
|
</div>
|
|
198
298
|
</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">
|