@qite/tide-booking-component 1.4.109 → 1.4.111
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 +3613 -2276
- package/build/build-cjs/src/booking-wizard/components/step-route.d.ts +2 -2
- package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +1 -0
- package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-util.d.ts +2 -1
- package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -31
- package/build/build-cjs/src/booking-wizard/features/travelers-form/travelers-form.d.ts +1 -2
- package/build/build-cjs/src/search-results/components/book-packaging-entry/index.d.ts +8 -0
- package/build/build-cjs/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
- package/build/build-cjs/src/search-results/components/spinner/spinner.d.ts +4 -1
- package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +7 -1
- package/build/build-cjs/src/search-results/types.d.ts +3 -0
- package/build/build-cjs/src/shared/booking/booking-panel.d.ts +13 -0
- package/build/build-cjs/src/shared/booking/shared-confirmation.d.ts +25 -0
- package/build/build-cjs/src/shared/booking/shared-sidebar.d.ts +34 -0
- package/build/build-cjs/src/shared/booking/step-indicators.d.ts +7 -0
- package/build/build-cjs/src/shared/booking/summary.d.ts +43 -0
- package/build/build-cjs/src/shared/booking/travelers-form.d.ts +93 -0
- package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +2 -0
- package/build/build-cjs/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
- package/build/build-cjs/src/shared/utils/booking-summary.d.ts +1 -0
- package/build/build-cjs/src/shared/utils/localization-util.d.ts +7 -0
- package/build/build-esm/index.js +3572 -2247
- package/build/build-esm/src/booking-wizard/components/step-route.d.ts +2 -2
- package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +1 -0
- package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-util.d.ts +2 -1
- package/build/build-esm/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -31
- package/build/build-esm/src/booking-wizard/features/travelers-form/travelers-form.d.ts +1 -2
- package/build/build-esm/src/search-results/components/book-packaging-entry/index.d.ts +8 -0
- package/build/build-esm/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
- package/build/build-esm/src/search-results/components/spinner/spinner.d.ts +4 -1
- package/build/build-esm/src/search-results/store/search-results-slice.d.ts +7 -1
- package/build/build-esm/src/search-results/types.d.ts +3 -0
- package/build/build-esm/src/shared/booking/booking-panel.d.ts +13 -0
- package/build/build-esm/src/shared/booking/shared-confirmation.d.ts +25 -0
- package/build/build-esm/src/shared/booking/shared-sidebar.d.ts +34 -0
- package/build/build-esm/src/shared/booking/step-indicators.d.ts +7 -0
- package/build/build-esm/src/shared/booking/summary.d.ts +43 -0
- package/build/build-esm/src/shared/booking/travelers-form.d.ts +93 -0
- package/build/build-esm/src/shared/components/flyin/flyin.d.ts +2 -0
- package/build/build-esm/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
- package/build/build-esm/src/shared/utils/booking-summary.d.ts +1 -0
- package/build/build-esm/src/shared/utils/localization-util.d.ts +7 -0
- package/package.json +2 -2
- package/src/booking-wizard/components/step-indicator.tsx +10 -31
- package/src/booking-wizard/components/step-route.tsx +39 -14
- package/src/booking-wizard/features/confirmation/confirmation.tsx +11 -55
- package/src/booking-wizard/features/sidebar/index.tsx +10 -4
- package/src/booking-wizard/features/sidebar/sidebar-flight.tsx +2 -2
- package/src/booking-wizard/features/sidebar/sidebar-util.ts +1 -5
- package/src/booking-wizard/features/sidebar/sidebar.tsx +331 -326
- package/src/booking-wizard/features/summary/summary.tsx +1 -1
- package/src/booking-wizard/features/travelers-form/travelers-form.tsx +84 -1010
- package/src/search-results/components/book-packaging-entry/index.tsx +229 -0
- package/src/search-results/components/book-packaging-entry/wl-sidebar.tsx +162 -0
- package/src/search-results/components/excursions/day-by-day-excursions.tsx +6 -2
- package/src/search-results/components/excursions/excursion-results.tsx +1 -1
- package/src/search-results/components/flight/flight-selection/independent-flight-selection.tsx +12 -3
- package/src/search-results/components/group-tour/group-tour-card.tsx +1 -1
- package/src/search-results/components/group-tour/group-tour-results.tsx +1 -1
- package/src/search-results/components/hotel/hotel-accommodation-results.tsx +6 -3
- package/src/search-results/components/itinerary/full-itinerary.tsx +1 -1
- package/src/search-results/components/itinerary/index.tsx +13 -12
- package/src/search-results/components/search-results-container/flight-search-results.tsx +1 -1
- package/src/search-results/components/search-results-container/search-results-container.tsx +280 -217
- package/src/search-results/components/spinner/spinner.tsx +12 -4
- package/src/search-results/store/search-results-slice.ts +22 -2
- package/src/search-results/types.ts +4 -0
- package/src/shared/booking/booking-panel.tsx +25 -0
- package/src/shared/booking/shared-confirmation.tsx +105 -0
- package/src/shared/booking/shared-sidebar.tsx +432 -0
- package/src/shared/booking/step-indicators.tsx +30 -0
- package/src/shared/booking/summary.tsx +380 -0
- package/src/shared/booking/travelers-form.tsx +870 -0
- package/src/shared/components/flyin/accommodation-flyin.tsx +3 -4
- package/src/shared/components/flyin/flights-flyin.tsx +1 -1
- package/src/shared/components/flyin/flyin.tsx +16 -9
- package/src/shared/components/flyin/group-tour-flyin.tsx +3 -4
- package/src/shared/components/flyin/packaging-flights-flyin.tsx +11 -4
- package/src/shared/components/icon.tsx +13 -0
- package/src/shared/translations/ar-SA.json +7 -1
- package/src/shared/translations/da-DK.json +7 -1
- package/src/shared/translations/de-DE.json +7 -1
- package/src/shared/translations/en-GB.json +8 -2
- package/src/shared/translations/es-ES.json +7 -1
- package/src/shared/translations/fr-BE.json +7 -1
- package/src/shared/translations/fr-FR.json +7 -1
- package/src/shared/translations/is-IS.json +7 -1
- package/src/shared/translations/it-IT.json +7 -1
- package/src/shared/translations/ja-JP.json +7 -1
- package/src/shared/translations/nl-BE.json +7 -1
- package/src/shared/translations/nl-NL.json +7 -1
- package/src/shared/translations/no-NO.json +7 -1
- package/src/shared/translations/pl-PL.json +7 -1
- package/src/shared/translations/pt-PT.json +7 -1
- package/src/shared/translations/sv-SE.json +7 -1
- package/src/shared/utils/booking-summary.tsx +46 -0
- package/src/shared/utils/localization-util.ts +8 -0
- package/src/shared/utils/tide-api-utils.ts +2 -2
- package/styles/components/_dropdown.scss +5 -0
- package/styles/components/_flyin.scss +43 -0
- package/styles/components/_loader.scss +82 -0
- package/styles/components/_search.scss +14 -2
- package/styles/content-blocks-variables.scss +14 -14
- /package/build/build-cjs/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
- /package/build/build-esm/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
- /package/src/{booking-wizard/components → shared/booking}/product-card.tsx +0 -0
|
@@ -56,6 +56,10 @@ export interface SearchResultsState {
|
|
|
56
56
|
excursionSearchParams: ExcursionSearchParams | null;
|
|
57
57
|
selectedExcursionSearchResult: PackagingAccommodationResponse | null;
|
|
58
58
|
confirmedExcursionsByDay: Record<string, PackagingAccommodationResponse[]>;
|
|
59
|
+
|
|
60
|
+
bookPackagingEntry: boolean;
|
|
61
|
+
currentStep: number;
|
|
62
|
+
bookingNumber?: string;
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
const initialState: SearchResultsState = {
|
|
@@ -103,7 +107,11 @@ const initialState: SearchResultsState = {
|
|
|
103
107
|
|
|
104
108
|
excursionSearchParams: null,
|
|
105
109
|
selectedExcursionSearchResult: null,
|
|
106
|
-
confirmedExcursionsByDay: {}
|
|
110
|
+
confirmedExcursionsByDay: {},
|
|
111
|
+
|
|
112
|
+
bookPackagingEntry: false,
|
|
113
|
+
currentStep: 0,
|
|
114
|
+
bookingNumber: undefined
|
|
107
115
|
};
|
|
108
116
|
|
|
109
117
|
const searchResultsSlice = createSlice({
|
|
@@ -280,6 +288,15 @@ const searchResultsSlice = createSlice({
|
|
|
280
288
|
},
|
|
281
289
|
clearConfirmedExcursionsForDay: (state, action: PayloadAction<{ dayKey: string }>) => {
|
|
282
290
|
delete state.confirmedExcursionsByDay[action.payload.dayKey];
|
|
291
|
+
},
|
|
292
|
+
setBookPackagingEntry(state, action: PayloadAction<boolean>) {
|
|
293
|
+
state.bookPackagingEntry = action.payload;
|
|
294
|
+
},
|
|
295
|
+
setCurrentStep(state, action: PayloadAction<number>) {
|
|
296
|
+
state.currentStep = action.payload;
|
|
297
|
+
},
|
|
298
|
+
setBookingNumber(state, action: PayloadAction<string>) {
|
|
299
|
+
state.bookingNumber = action.payload;
|
|
283
300
|
}
|
|
284
301
|
}
|
|
285
302
|
});
|
|
@@ -325,7 +342,10 @@ export const {
|
|
|
325
342
|
setSelectedExcursionSearchResult,
|
|
326
343
|
confirmExcursionForDay,
|
|
327
344
|
removeConfirmedExcursionForDay,
|
|
328
|
-
clearConfirmedExcursionsForDay
|
|
345
|
+
clearConfirmedExcursionsForDay,
|
|
346
|
+
setBookPackagingEntry,
|
|
347
|
+
setCurrentStep,
|
|
348
|
+
setBookingNumber
|
|
329
349
|
} = searchResultsSlice.actions;
|
|
330
350
|
|
|
331
351
|
export default searchResultsSlice.reducer;
|
|
@@ -61,6 +61,10 @@ export interface SearchResultsConfiguration {
|
|
|
61
61
|
destinationImage?: { url: string; alt: string };
|
|
62
62
|
onBook?: (result: BookingPackage) => void;
|
|
63
63
|
packagingEntry?: PackagingEntry | null;
|
|
64
|
+
|
|
65
|
+
generatePaymentUrl?: boolean;
|
|
66
|
+
entryStatus?: number;
|
|
67
|
+
customEntryStatusId?: number;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
export type FilterType = 'checkbox' | 'toggle' | 'slider' | 'star-rating';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface BookingPanelProps {
|
|
4
|
+
currentStep: number;
|
|
5
|
+
stepLabels: string[];
|
|
6
|
+
renderTitle: (step: number) => React.ReactNode;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
StepIndicatorsComponent: React.ComponentType<{ currentStep: number; stepLabels: string[] }>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const BookingPanel: React.FC<BookingPanelProps> = ({ currentStep, stepLabels, renderTitle, children, StepIndicatorsComponent }) => {
|
|
12
|
+
return (
|
|
13
|
+
<div className="booking__panel">
|
|
14
|
+
<StepIndicatorsComponent currentStep={currentStep} stepLabels={stepLabels} />
|
|
15
|
+
<div className="booking__panel-frame booking__panel-frame--transparent">
|
|
16
|
+
<div className="booking__panel-heading">
|
|
17
|
+
<h4 className="booking__panel-title">{renderTitle(currentStep)}</h4>
|
|
18
|
+
</div>
|
|
19
|
+
<div className="booking__panel-body">{children}</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default BookingPanel;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Icon from '../components/icon';
|
|
3
|
+
import Message from '../../booking-wizard/components/message';
|
|
4
|
+
|
|
5
|
+
export interface SharedConfirmationTranslations {
|
|
6
|
+
MAIL_SUBJECT: string;
|
|
7
|
+
TITLE_TEXT_OPTION: string;
|
|
8
|
+
TITLE_TEXT_OFFER: string;
|
|
9
|
+
TITLE_TEXT_BOOKING: string;
|
|
10
|
+
MESSAGE_TEXT1: string;
|
|
11
|
+
MESSAGE_TEXT2_OFFER: string;
|
|
12
|
+
MESSAGE_TEXT2_BOOKING: string;
|
|
13
|
+
QUESTIONS_TEXT1: string;
|
|
14
|
+
QUESTIONS_TEXT2: string;
|
|
15
|
+
QUESTIONS_TEXT3: string;
|
|
16
|
+
QUESTIONS_ALT: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SharedConfirmationProps {
|
|
20
|
+
bookingNumber?: string;
|
|
21
|
+
isOption?: boolean;
|
|
22
|
+
isOffer?: boolean;
|
|
23
|
+
translations: SharedConfirmationTranslations;
|
|
24
|
+
companyContactPhone?: string;
|
|
25
|
+
companyContactEmail?: string;
|
|
26
|
+
homeUrl?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function format(str: string, args: (string | number | undefined)[]): string {
|
|
30
|
+
// Simple format function: replaces {0}, {1}, ... with args
|
|
31
|
+
return str.replace(/\{(\d+)\}/g, (match, number) => (typeof args[number] !== 'undefined' ? String(args[number]) : match));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const SharedConfirmation: React.FC<SharedConfirmationProps> = ({
|
|
35
|
+
bookingNumber,
|
|
36
|
+
isOption,
|
|
37
|
+
isOffer,
|
|
38
|
+
translations,
|
|
39
|
+
companyContactPhone,
|
|
40
|
+
companyContactEmail,
|
|
41
|
+
homeUrl = '/'
|
|
42
|
+
}) => {
|
|
43
|
+
const encodedMailSubject = encodeURI(translations.MAIL_SUBJECT);
|
|
44
|
+
const titleText = isOption
|
|
45
|
+
? format(translations.TITLE_TEXT_OPTION, [bookingNumber])
|
|
46
|
+
: isOffer
|
|
47
|
+
? format(translations.TITLE_TEXT_OFFER, [bookingNumber])
|
|
48
|
+
: format(translations.TITLE_TEXT_BOOKING, [bookingNumber]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="form form__booking--message" id="booking--confirmation">
|
|
52
|
+
<div className="form__region">
|
|
53
|
+
<div className="form__row">
|
|
54
|
+
<div className="form__group">
|
|
55
|
+
<Message
|
|
56
|
+
type="success"
|
|
57
|
+
title={titleText}
|
|
58
|
+
actionComponent={
|
|
59
|
+
companyContactPhone || companyContactEmail ? (
|
|
60
|
+
<div className="sm">
|
|
61
|
+
{companyContactPhone && (
|
|
62
|
+
<a href={`tel://${companyContactPhone}`} className="sm__icon">
|
|
63
|
+
<Icon name="tel" />
|
|
64
|
+
</a>
|
|
65
|
+
)}
|
|
66
|
+
{companyContactEmail && (
|
|
67
|
+
<a href={`mailto://${companyContactEmail}`} className="sm__icon">
|
|
68
|
+
<Icon name="mail" />
|
|
69
|
+
</a>
|
|
70
|
+
)}
|
|
71
|
+
{homeUrl && (
|
|
72
|
+
<a href={homeUrl} className="sm__icon">
|
|
73
|
+
<Icon name="home" />
|
|
74
|
+
</a>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
) : undefined
|
|
78
|
+
}>
|
|
79
|
+
{!isOption ? (
|
|
80
|
+
<>
|
|
81
|
+
<p>
|
|
82
|
+
{translations.MESSAGE_TEXT1}
|
|
83
|
+
<br />
|
|
84
|
+
{isOffer ? translations.MESSAGE_TEXT2_OFFER : translations.MESSAGE_TEXT2_BOOKING}
|
|
85
|
+
</p>
|
|
86
|
+
{companyContactEmail && (
|
|
87
|
+
<p>
|
|
88
|
+
{translations.QUESTIONS_TEXT1}{' '}
|
|
89
|
+
<a href={`mailto:${companyContactEmail}?subject=${encodedMailSubject}`} title={translations.QUESTIONS_ALT}>
|
|
90
|
+
{translations.QUESTIONS_TEXT2}
|
|
91
|
+
</a>
|
|
92
|
+
{translations.QUESTIONS_TEXT3}
|
|
93
|
+
</p>
|
|
94
|
+
)}
|
|
95
|
+
</>
|
|
96
|
+
) : undefined}
|
|
97
|
+
</Message>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export default SharedConfirmation;
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { formatPrice } from '../utils/localization-util';
|
|
3
|
+
import { BookingPackageRoom, PackagingEntryLine, ServiceType } from '@qite/tide-client';
|
|
4
|
+
import { buildClassName } from '../utils/class-util';
|
|
5
|
+
import ProductCard from './product-card';
|
|
6
|
+
import { compact, isEmpty } from 'lodash';
|
|
7
|
+
import SidebarFlight from '../../booking-wizard/features/sidebar/sidebar-flight';
|
|
8
|
+
import { getDatePeriodText, getPaxTypeTranslation } from '../../booking-wizard/features/sidebar/sidebar-util';
|
|
9
|
+
import { PricePerPaxType } from '../../booking-wizard/types';
|
|
10
|
+
|
|
11
|
+
interface SharedSidebarProps {
|
|
12
|
+
productName: string;
|
|
13
|
+
thumbnailUrl?: string;
|
|
14
|
+
isLoading?: boolean;
|
|
15
|
+
travelerRooms?: string[];
|
|
16
|
+
startDateText?: string;
|
|
17
|
+
endDateText?: string;
|
|
18
|
+
departureFlightMetaData?: any;
|
|
19
|
+
returnFlightMetaData?: any;
|
|
20
|
+
basePrice?: number;
|
|
21
|
+
commission?: number;
|
|
22
|
+
totalPrice?: number;
|
|
23
|
+
remainingAmountText?: string;
|
|
24
|
+
includedCosts?: any[];
|
|
25
|
+
extraCosts?: any[];
|
|
26
|
+
deposit?: number;
|
|
27
|
+
accommodations?: BookingPackageRoom[];
|
|
28
|
+
packagingAccommodations?: PackagingEntryLine[];
|
|
29
|
+
includedServiceTypes?: number[];
|
|
30
|
+
isOnRequest?: boolean;
|
|
31
|
+
headerComponent?: React.ReactNode;
|
|
32
|
+
footerComponent?: React.ReactNode;
|
|
33
|
+
loaderComponent?: React.ReactNode;
|
|
34
|
+
isUnavailable?: boolean;
|
|
35
|
+
basePricePerPaxType?: PricePerPaxType[];
|
|
36
|
+
seperateExtraPricePerPaxType?: PricePerPaxType[];
|
|
37
|
+
translations: any;
|
|
38
|
+
agent?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const SharedSidebar: React.FC<SharedSidebarProps> = ({
|
|
42
|
+
productName,
|
|
43
|
+
thumbnailUrl,
|
|
44
|
+
isLoading,
|
|
45
|
+
translations,
|
|
46
|
+
headerComponent,
|
|
47
|
+
travelerRooms,
|
|
48
|
+
startDateText,
|
|
49
|
+
endDateText,
|
|
50
|
+
loaderComponent,
|
|
51
|
+
departureFlightMetaData,
|
|
52
|
+
returnFlightMetaData,
|
|
53
|
+
accommodations,
|
|
54
|
+
packagingAccommodations,
|
|
55
|
+
isOnRequest,
|
|
56
|
+
includedServiceTypes,
|
|
57
|
+
basePrice,
|
|
58
|
+
commission,
|
|
59
|
+
totalPrice,
|
|
60
|
+
includedCosts,
|
|
61
|
+
extraCosts,
|
|
62
|
+
deposit,
|
|
63
|
+
basePricePerPaxType,
|
|
64
|
+
seperateExtraPricePerPaxType,
|
|
65
|
+
isUnavailable,
|
|
66
|
+
agent,
|
|
67
|
+
footerComponent
|
|
68
|
+
}) => {
|
|
69
|
+
if (!translations) return null;
|
|
70
|
+
|
|
71
|
+
const [active, setActive] = useState<boolean>(false);
|
|
72
|
+
|
|
73
|
+
const handleToggleClick = () => {
|
|
74
|
+
setActive(!active);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const isFlightOnly = includedServiceTypes?.length === 1 && includedServiceTypes.includes(ServiceType.flight);
|
|
78
|
+
|
|
79
|
+
const canShowPriceBreakdownSection = Boolean(basePrice && basePrice > 0) || !isEmpty(includedCosts) || !isEmpty(extraCosts);
|
|
80
|
+
const canShowTotalPriceSection = Boolean(totalPrice && totalPrice > 0);
|
|
81
|
+
|
|
82
|
+
const remainingAmount = Number(((totalPrice ?? 0) - (deposit ?? 0)).toFixed(2));
|
|
83
|
+
|
|
84
|
+
// TODO: Determine currency code based on the data, for now default to EUR
|
|
85
|
+
let currencyCode = 'EUR';
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className={buildClassName(['booking__sidebar', active && 'booking__sidebar--active'])}>
|
|
89
|
+
{headerComponent}
|
|
90
|
+
|
|
91
|
+
<div className="booking__sidebar-frame">
|
|
92
|
+
<ProductCard productName={productName} thumbnailUrl={thumbnailUrl} handleToggleClick={handleToggleClick} />
|
|
93
|
+
<div className="pricing-summary">
|
|
94
|
+
<div className="pricing-summary__wrapper">
|
|
95
|
+
<div className="pricing-summary__region pricing-summary__region--fade-in">
|
|
96
|
+
<div className="pricing-summary__group">
|
|
97
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.TRAVEL_INFO}</h6>
|
|
98
|
+
{!isEmpty(travelerRooms) &&
|
|
99
|
+
travelerRooms?.map((room, rIndex) => (
|
|
100
|
+
<div className="pricing-summary__row" key={rIndex}>
|
|
101
|
+
<div className="pricing-summary__property">
|
|
102
|
+
{travelerRooms.length > 1 && `${translations.SHARED.ROOM} ${rIndex + 1}`}
|
|
103
|
+
{travelerRooms.length === 1 && translations.ROOM_OPTIONS_FORM.TRAVELER_GROUP}
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div className="pricing-summary__value">{room}</div>
|
|
107
|
+
</div>
|
|
108
|
+
))}
|
|
109
|
+
{startDateText && (
|
|
110
|
+
<div className="pricing-summary__row">
|
|
111
|
+
<div className="pricing-summary__property">
|
|
112
|
+
{startDateText && endDateText ? translations.SIDEBAR.DEPARTURE : translations.SIDEBAR.DEPARTURE_SINGLE}
|
|
113
|
+
</div>
|
|
114
|
+
<div className="pricing-summary__value">{startDateText}</div>
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
|
|
118
|
+
{endDateText && (
|
|
119
|
+
<div className="pricing-summary__row">
|
|
120
|
+
<div className="pricing-summary__property">{translations.SIDEBAR.ARRIVAL}</div>
|
|
121
|
+
<div className="pricing-summary__value">{endDateText}</div>
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
{/* {lines.map((line: any, idx: number) => (
|
|
126
|
+
<div className="pricing-summary__row" key={line.guid || idx}>
|
|
127
|
+
<div className="pricing-summary__property">
|
|
128
|
+
{line.serviceType === 3 && (
|
|
129
|
+
<>
|
|
130
|
+
{line.productName} <br />
|
|
131
|
+
{line.accommodationName && <span>{line.accommodationName}</span>}
|
|
132
|
+
<br />
|
|
133
|
+
{line.from && line.to && (
|
|
134
|
+
<span>
|
|
135
|
+
{new Date(line.from).toLocaleDateString()} - {new Date(line.to).toLocaleDateString()}
|
|
136
|
+
</span>
|
|
137
|
+
)}
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
{line.serviceType === 7 && line.flightInformation && (
|
|
141
|
+
<>
|
|
142
|
+
<strong>{line.productName}</strong>
|
|
143
|
+
<br />
|
|
144
|
+
{line.flightInformation.flightLines &&
|
|
145
|
+
line.flightInformation.flightLines.map((flight: any, fIdx: number) => (
|
|
146
|
+
<div key={fIdx} style={{ marginBottom: 4 }}>
|
|
147
|
+
{flight.airlineDescription} {flight.flightNumber}
|
|
148
|
+
<br />
|
|
149
|
+
{flight.departureAirportDescription} ({flight.departureAirportCode}) {flight.departureTime} → {flight.arrivalAirportDescription}{' '}
|
|
150
|
+
({flight.arrivalAirportCode}) {flight.arrivalTime}
|
|
151
|
+
</div>
|
|
152
|
+
))}
|
|
153
|
+
</>
|
|
154
|
+
)}
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
))} */}
|
|
158
|
+
</div>
|
|
159
|
+
{isLoading && loaderComponent}
|
|
160
|
+
|
|
161
|
+
{!isLoading && departureFlightMetaData && (
|
|
162
|
+
<SidebarFlight title={translations.SIDEBAR.DEPARTURE_FLIGHT} flightMetaData={departureFlightMetaData} translations={translations} />
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{!isLoading && returnFlightMetaData && (
|
|
166
|
+
<SidebarFlight title={translations.SIDEBAR.ARRIVAL_FLIGHT} flightMetaData={returnFlightMetaData} translations={translations} />
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
{accommodations && (
|
|
170
|
+
<div className="pricing-summary__group">
|
|
171
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.ACCOMMODATION}</h6>
|
|
172
|
+
{accommodations.map((accommodation) => {
|
|
173
|
+
let option = accommodation.options.find((x) => x.isSelected);
|
|
174
|
+
return (
|
|
175
|
+
<div key={accommodation.index}>
|
|
176
|
+
<div className="pricing-summary__row">
|
|
177
|
+
<div className="pricing-summary__property">
|
|
178
|
+
{option?.accommodationName}
|
|
179
|
+
{isOnRequest ? ` (${translations.SIDEBAR.ON_REQUEST})` : ''}
|
|
180
|
+
{option?.isOnRequest ? ` (${translations.SIDEBAR.ON_REQUEST})` : ''}
|
|
181
|
+
</div>
|
|
182
|
+
{/*<div className="pricing-summary__value">
|
|
183
|
+
{option && option.price > 0 && formatPrice(option?.price)}
|
|
184
|
+
</div>*/}
|
|
185
|
+
</div>
|
|
186
|
+
<div className="pricing-summary__row">
|
|
187
|
+
<div className="price-summarty__property">{option?.regimeName}</div>
|
|
188
|
+
<div className="price-summary__value">{!isFlightOnly && getDatePeriodText(translations, option?.from, option?.to, true)}</div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
})}
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
195
|
+
{packagingAccommodations && (
|
|
196
|
+
<div className="pricing-summary__group">
|
|
197
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.ACCOMMODATION}</h6>
|
|
198
|
+
{packagingAccommodations.map((accommodation) => {
|
|
199
|
+
return (
|
|
200
|
+
<div key={accommodation.guid}>
|
|
201
|
+
<div className="pricing-summary__row">
|
|
202
|
+
<div className="pricing-summary__property">
|
|
203
|
+
{accommodation?.accommodationName}
|
|
204
|
+
{isOnRequest ? ` (${translations.SIDEBAR.ON_REQUEST})` : ''}
|
|
205
|
+
</div>
|
|
206
|
+
{/*<div className="pricing-summary__value">
|
|
207
|
+
{option && option.price > 0 && formatPrice(option?.price)}
|
|
208
|
+
</div>*/}
|
|
209
|
+
</div>
|
|
210
|
+
<div className="pricing-summary__row">
|
|
211
|
+
<div className="price-summarty__property">{accommodation?.regimeName}</div>
|
|
212
|
+
<div className="price-summary__value">
|
|
213
|
+
{!isFlightOnly && getDatePeriodText(translations, accommodation?.from, accommodation?.to, true)}
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
})}
|
|
219
|
+
</div>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
{!isLoading && canShowPriceBreakdownSection && (
|
|
224
|
+
<div className={`pricing-summary__region ${!isLoading ? 'pricing-summary__region--fade-in' : ''}`}>
|
|
225
|
+
{basePrice !== undefined && basePrice > 0 && (
|
|
226
|
+
<div className="pricing-summary__group">
|
|
227
|
+
<div className="pricing-summary__row">
|
|
228
|
+
<div className="pricing-summary__property">
|
|
229
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.BASE_PRICE}</h6>
|
|
230
|
+
</div>
|
|
231
|
+
<div className="pricing-summary__value">{formatPrice(basePrice, currencyCode)}</div>
|
|
232
|
+
</div>
|
|
233
|
+
{basePricePerPaxType &&
|
|
234
|
+
basePricePerPaxType.map((ppt, index) => (
|
|
235
|
+
<React.Fragment key={`${ppt.paxType}-${index}`}>
|
|
236
|
+
<div className="pricing-summary__row">
|
|
237
|
+
<div className="pricing-summary__property">
|
|
238
|
+
{ppt.numberOfPax} {getPaxTypeTranslation(translations, ppt.paxType, ppt.numberOfPax)}
|
|
239
|
+
</div>
|
|
240
|
+
<div className="pricing-summary__value">{formatPrice(ppt.pricePerPaxType, currencyCode)}</div>
|
|
241
|
+
</div>
|
|
242
|
+
{ppt.details.map((detail, dIndex) => (
|
|
243
|
+
<div className="pricing-summary__row pricing-summary__row--sub" key={`${ppt.paxType}-${index}-${dIndex}`}>
|
|
244
|
+
<div className="pricing-summary__property">
|
|
245
|
+
{detail.numberOfPax}x {detail.description}
|
|
246
|
+
</div>
|
|
247
|
+
<div className="pricing-summary__value">{formatPrice(detail.price / detail.numberOfPax, currencyCode)}</div>
|
|
248
|
+
</div>
|
|
249
|
+
))}
|
|
250
|
+
</React.Fragment>
|
|
251
|
+
))}
|
|
252
|
+
</div>
|
|
253
|
+
)}
|
|
254
|
+
{!isEmpty(includedCosts) && (
|
|
255
|
+
<div className="pricing-summary__group">
|
|
256
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.INCLUDED_COSTS}</h6>
|
|
257
|
+
{includedCosts?.map((priceDetail, index) => (
|
|
258
|
+
<React.Fragment key={compact([priceDetail.productCode, priceDetail.accommodationCode, index]).join('_')}>
|
|
259
|
+
<div className="pricing-summary__row">
|
|
260
|
+
<div className="pricing-summary__property">{priceDetail.productName}</div>
|
|
261
|
+
{priceDetail.showPrice && (
|
|
262
|
+
<div className="pricing-summary__value">{formatPrice(priceDetail.price * priceDetail.amount, currencyCode)}</div>
|
|
263
|
+
)}
|
|
264
|
+
</div>
|
|
265
|
+
<div className="pricing-summary__row">
|
|
266
|
+
<div className="price-summary__property">{priceDetail.accommodationName ?? priceDetail.accommodationCode}</div>
|
|
267
|
+
</div>
|
|
268
|
+
</React.Fragment>
|
|
269
|
+
))}
|
|
270
|
+
</div>
|
|
271
|
+
)}
|
|
272
|
+
{!isEmpty(extraCosts) && (
|
|
273
|
+
<div className="pricing-summary__group">
|
|
274
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.EXTRA_COSTS}</h6>
|
|
275
|
+
{extraCosts?.map((priceDetail, index) => (
|
|
276
|
+
<React.Fragment key={compact([priceDetail.productCode, priceDetail.accommodationCode, index]).join('_')}>
|
|
277
|
+
<div className="pricing-summary__row">
|
|
278
|
+
<div className="pricing-summary__property">{priceDetail.productName}</div>
|
|
279
|
+
{priceDetail.showPrice && (
|
|
280
|
+
<div className="pricing-summary__value">{formatPrice(priceDetail.price * priceDetail.amount, currencyCode)}</div>
|
|
281
|
+
)}
|
|
282
|
+
</div>
|
|
283
|
+
<div className="pricing-summary__row">
|
|
284
|
+
<div className="pricing-summary__property">{priceDetail.accommodationName ?? priceDetail.accommodationCode}</div>
|
|
285
|
+
</div>
|
|
286
|
+
{seperateExtraPricePerPaxType &&
|
|
287
|
+
seperateExtraPricePerPaxType.map((ppt, index) => (
|
|
288
|
+
<React.Fragment key={`${ppt.paxType}-${index}`}>
|
|
289
|
+
<div className="pricing-summary__row">
|
|
290
|
+
<div className="pricing-summary__property">
|
|
291
|
+
{ppt.numberOfPax} {getPaxTypeTranslation(translations, ppt.paxType, ppt.numberOfPax)}
|
|
292
|
+
</div>
|
|
293
|
+
<div className="pricing-summary__value">{formatPrice(ppt.pricePerPaxType, currencyCode)}</div>
|
|
294
|
+
</div>
|
|
295
|
+
{ppt.details.map((detail, dIndex) => (
|
|
296
|
+
<div className="pricing-summary__row pricing-summary__row--sub" key={`${ppt.paxType}-${index}-${dIndex}`}>
|
|
297
|
+
<div className="pricing-summary__property">
|
|
298
|
+
{detail.numberOfPax}x {detail.description}
|
|
299
|
+
</div>
|
|
300
|
+
<div className="pricing-summary__value">{formatPrice(detail.price / detail.numberOfPax, currencyCode)}</div>
|
|
301
|
+
</div>
|
|
302
|
+
))}
|
|
303
|
+
</React.Fragment>
|
|
304
|
+
))}
|
|
305
|
+
</React.Fragment>
|
|
306
|
+
))}
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
310
|
+
)}
|
|
311
|
+
|
|
312
|
+
{!isLoading && canShowTotalPriceSection && !isUnavailable && (
|
|
313
|
+
<div className={`pricing-summary__region pricing-summary__region--pricing ${!isLoading ? 'pricing-summary__region--fade-in' : ''}`}>
|
|
314
|
+
{deposit && remainingAmount > 0 ? (
|
|
315
|
+
<div className="pricing-summary__group">
|
|
316
|
+
{agent && (
|
|
317
|
+
<div className="pricing-summary__row pricing-summary__row--total-price">
|
|
318
|
+
<div className="pricing-summary__property">
|
|
319
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.COMMISSION}</h6>
|
|
320
|
+
</div>
|
|
321
|
+
<div className="pricing-summary__value">
|
|
322
|
+
<div className="pricing">
|
|
323
|
+
<div className="pricing__price">{formatPrice(commission ?? 0, currencyCode)}</div>
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
)}
|
|
328
|
+
{totalPrice !== undefined && totalPrice > 0 && (
|
|
329
|
+
<div className="pricing-summary__row pricing-summary__row--total-price">
|
|
330
|
+
<div className="pricing-summary__property">
|
|
331
|
+
<h6 className="pricing-summary__title">{translations.SHARED.TOTAL_PRICE}</h6>
|
|
332
|
+
</div>
|
|
333
|
+
<div className="pricing-summary__value">
|
|
334
|
+
<div className="pricing">
|
|
335
|
+
<div className="pricing__price">{formatPrice(totalPrice, currencyCode)}</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
)}
|
|
340
|
+
<div className="pricing-summary__row">
|
|
341
|
+
<div className="pricing-summary__property">
|
|
342
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.DEPOSIT}</h6>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<div className="pricing-summary__value">
|
|
346
|
+
<div className="pricing">
|
|
347
|
+
<div className="pricing__price">{formatPrice(deposit, currencyCode)}</div>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
<div className="pricing-summary__row">
|
|
352
|
+
<small>
|
|
353
|
+
<em>
|
|
354
|
+
{translations.SIDEBAR.DEPOSIT_TEXT1}
|
|
355
|
+
<strong>{translations.SIDEBAR.DEPOSIT_TEXT2}</strong>
|
|
356
|
+
{translations.SIDEBAR.DEPOSIT_TEXT3}
|
|
357
|
+
{formatPrice(remainingAmount, currencyCode)}
|
|
358
|
+
{translations.SIDEBAR.DEPOSIT_TEXT4}
|
|
359
|
+
<strong>{translations.SIDEBAR.DEPOSIT_TEXT5}</strong>
|
|
360
|
+
{translations.SIDEBAR.DEPOSIT_TEXT6}
|
|
361
|
+
</em>
|
|
362
|
+
</small>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
) : (
|
|
366
|
+
<div className="pricing-summary__group">
|
|
367
|
+
{agent && (
|
|
368
|
+
<div className="pricing-summary__row pricing-summary__row--total-price">
|
|
369
|
+
<div className="pricing-summary__property">
|
|
370
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.COMMISSION}</h6>
|
|
371
|
+
</div>
|
|
372
|
+
<div className="pricing-summary__value">
|
|
373
|
+
<div className="pricing">
|
|
374
|
+
<div className="pricing__price">{formatPrice(commission ?? 0, currencyCode)}</div>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
)}
|
|
379
|
+
{totalPrice !== undefined && totalPrice > 0 && (
|
|
380
|
+
<div className="pricing-summary__row pricing-summary__row--total-price">
|
|
381
|
+
<div className="pricing-summary__property">
|
|
382
|
+
<h6 className="pricing-summary__title">{translations.SHARED.TOTAL_PRICE}</h6>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
<div className="pricing-summary__value">
|
|
386
|
+
<div className="pricing">
|
|
387
|
+
<div className="pricing__price">{formatPrice(totalPrice, currencyCode)}</div>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
</div>
|
|
393
|
+
)}
|
|
394
|
+
</div>
|
|
395
|
+
)}
|
|
396
|
+
|
|
397
|
+
{footerComponent}
|
|
398
|
+
|
|
399
|
+
{/* <div className="pricing-summary__region pricing-summary__region--pricing pricing-summary__region--fade-in">
|
|
400
|
+
<div className="pricing-summary__group">
|
|
401
|
+
<div className="pricing-summary__row pricing-summary__row--total-price">
|
|
402
|
+
<div className="pricing-summary__property">
|
|
403
|
+
<h6 className="pricing-summary__title">{translations.SHARED.TOTAL_PRICE}</h6>
|
|
404
|
+
</div>
|
|
405
|
+
<div className="pricing-summary__value">
|
|
406
|
+
<div className="pricing">
|
|
407
|
+
<div className="pricing__price">{formatPrice(price || 0, 'EUR')}</div>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
{depositAmount > 0 && (
|
|
412
|
+
<div className="pricing-summary__row">
|
|
413
|
+
<div className="pricing-summary__property">
|
|
414
|
+
<h6 className="pricing-summary__title">{translations.SIDEBAR.DEPOSIT}</h6>
|
|
415
|
+
</div>
|
|
416
|
+
<div className="pricing-summary__value">
|
|
417
|
+
<div className="pricing">
|
|
418
|
+
<div className="pricing__price">{formatPrice(depositAmount, 'EUR')}</div>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
)}
|
|
423
|
+
</div>
|
|
424
|
+
</div> */}
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
</div>
|
|
429
|
+
);
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
export default SharedSidebar;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { buildClassName } from '../utils/class-util';
|
|
3
|
+
|
|
4
|
+
interface StepIndicatorsProps {
|
|
5
|
+
currentStep: number;
|
|
6
|
+
stepLabels: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const StepIndicators: React.FC<StepIndicatorsProps> = ({ currentStep, stepLabels }) => {
|
|
10
|
+
return (
|
|
11
|
+
<div className="step-indicators">
|
|
12
|
+
<div className="step-indicators__items">
|
|
13
|
+
{stepLabels.map((stepName, index) => (
|
|
14
|
+
<div
|
|
15
|
+
key={`${index + 1}-${stepName}`}
|
|
16
|
+
className={buildClassName([
|
|
17
|
+
'step-indicators__item',
|
|
18
|
+
currentStep === index && 'step-indicators__item--active',
|
|
19
|
+
currentStep > index && 'step-indicators__item--completed'
|
|
20
|
+
])}>
|
|
21
|
+
<div className="step-indicators__icon">{index + 1}</div>
|
|
22
|
+
<div className="step-indicators__text">{stepName}</div>
|
|
23
|
+
</div>
|
|
24
|
+
))}
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default StepIndicators;
|