@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.
Files changed (106) hide show
  1. package/build/build-cjs/index.js +3613 -2276
  2. package/build/build-cjs/src/booking-wizard/components/step-route.d.ts +2 -2
  3. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +1 -0
  4. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-util.d.ts +2 -1
  5. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -31
  6. package/build/build-cjs/src/booking-wizard/features/travelers-form/travelers-form.d.ts +1 -2
  7. package/build/build-cjs/src/search-results/components/book-packaging-entry/index.d.ts +8 -0
  8. package/build/build-cjs/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
  9. package/build/build-cjs/src/search-results/components/spinner/spinner.d.ts +4 -1
  10. package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +7 -1
  11. package/build/build-cjs/src/search-results/types.d.ts +3 -0
  12. package/build/build-cjs/src/shared/booking/booking-panel.d.ts +13 -0
  13. package/build/build-cjs/src/shared/booking/shared-confirmation.d.ts +25 -0
  14. package/build/build-cjs/src/shared/booking/shared-sidebar.d.ts +34 -0
  15. package/build/build-cjs/src/shared/booking/step-indicators.d.ts +7 -0
  16. package/build/build-cjs/src/shared/booking/summary.d.ts +43 -0
  17. package/build/build-cjs/src/shared/booking/travelers-form.d.ts +93 -0
  18. package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +2 -0
  19. package/build/build-cjs/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
  20. package/build/build-cjs/src/shared/utils/booking-summary.d.ts +1 -0
  21. package/build/build-cjs/src/shared/utils/localization-util.d.ts +7 -0
  22. package/build/build-esm/index.js +3572 -2247
  23. package/build/build-esm/src/booking-wizard/components/step-route.d.ts +2 -2
  24. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +1 -0
  25. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-util.d.ts +2 -1
  26. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -31
  27. package/build/build-esm/src/booking-wizard/features/travelers-form/travelers-form.d.ts +1 -2
  28. package/build/build-esm/src/search-results/components/book-packaging-entry/index.d.ts +8 -0
  29. package/build/build-esm/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +7 -0
  30. package/build/build-esm/src/search-results/components/spinner/spinner.d.ts +4 -1
  31. package/build/build-esm/src/search-results/store/search-results-slice.d.ts +7 -1
  32. package/build/build-esm/src/search-results/types.d.ts +3 -0
  33. package/build/build-esm/src/shared/booking/booking-panel.d.ts +13 -0
  34. package/build/build-esm/src/shared/booking/shared-confirmation.d.ts +25 -0
  35. package/build/build-esm/src/shared/booking/shared-sidebar.d.ts +34 -0
  36. package/build/build-esm/src/shared/booking/step-indicators.d.ts +7 -0
  37. package/build/build-esm/src/shared/booking/summary.d.ts +43 -0
  38. package/build/build-esm/src/shared/booking/travelers-form.d.ts +93 -0
  39. package/build/build-esm/src/shared/components/flyin/flyin.d.ts +2 -0
  40. package/build/build-esm/src/shared/components/flyin/packaging-flights-flyin.d.ts +2 -0
  41. package/build/build-esm/src/shared/utils/booking-summary.d.ts +1 -0
  42. package/build/build-esm/src/shared/utils/localization-util.d.ts +7 -0
  43. package/package.json +2 -2
  44. package/src/booking-wizard/components/step-indicator.tsx +10 -31
  45. package/src/booking-wizard/components/step-route.tsx +39 -14
  46. package/src/booking-wizard/features/confirmation/confirmation.tsx +11 -55
  47. package/src/booking-wizard/features/sidebar/index.tsx +10 -4
  48. package/src/booking-wizard/features/sidebar/sidebar-flight.tsx +2 -2
  49. package/src/booking-wizard/features/sidebar/sidebar-util.ts +1 -5
  50. package/src/booking-wizard/features/sidebar/sidebar.tsx +331 -326
  51. package/src/booking-wizard/features/summary/summary.tsx +1 -1
  52. package/src/booking-wizard/features/travelers-form/travelers-form.tsx +84 -1010
  53. package/src/search-results/components/book-packaging-entry/index.tsx +229 -0
  54. package/src/search-results/components/book-packaging-entry/wl-sidebar.tsx +162 -0
  55. package/src/search-results/components/excursions/day-by-day-excursions.tsx +6 -2
  56. package/src/search-results/components/excursions/excursion-results.tsx +1 -1
  57. package/src/search-results/components/flight/flight-selection/independent-flight-selection.tsx +12 -3
  58. package/src/search-results/components/group-tour/group-tour-card.tsx +1 -1
  59. package/src/search-results/components/group-tour/group-tour-results.tsx +1 -1
  60. package/src/search-results/components/hotel/hotel-accommodation-results.tsx +6 -3
  61. package/src/search-results/components/itinerary/full-itinerary.tsx +1 -1
  62. package/src/search-results/components/itinerary/index.tsx +13 -12
  63. package/src/search-results/components/search-results-container/flight-search-results.tsx +1 -1
  64. package/src/search-results/components/search-results-container/search-results-container.tsx +280 -217
  65. package/src/search-results/components/spinner/spinner.tsx +12 -4
  66. package/src/search-results/store/search-results-slice.ts +22 -2
  67. package/src/search-results/types.ts +4 -0
  68. package/src/shared/booking/booking-panel.tsx +25 -0
  69. package/src/shared/booking/shared-confirmation.tsx +105 -0
  70. package/src/shared/booking/shared-sidebar.tsx +432 -0
  71. package/src/shared/booking/step-indicators.tsx +30 -0
  72. package/src/shared/booking/summary.tsx +380 -0
  73. package/src/shared/booking/travelers-form.tsx +870 -0
  74. package/src/shared/components/flyin/accommodation-flyin.tsx +3 -4
  75. package/src/shared/components/flyin/flights-flyin.tsx +1 -1
  76. package/src/shared/components/flyin/flyin.tsx +16 -9
  77. package/src/shared/components/flyin/group-tour-flyin.tsx +3 -4
  78. package/src/shared/components/flyin/packaging-flights-flyin.tsx +11 -4
  79. package/src/shared/components/icon.tsx +13 -0
  80. package/src/shared/translations/ar-SA.json +7 -1
  81. package/src/shared/translations/da-DK.json +7 -1
  82. package/src/shared/translations/de-DE.json +7 -1
  83. package/src/shared/translations/en-GB.json +8 -2
  84. package/src/shared/translations/es-ES.json +7 -1
  85. package/src/shared/translations/fr-BE.json +7 -1
  86. package/src/shared/translations/fr-FR.json +7 -1
  87. package/src/shared/translations/is-IS.json +7 -1
  88. package/src/shared/translations/it-IT.json +7 -1
  89. package/src/shared/translations/ja-JP.json +7 -1
  90. package/src/shared/translations/nl-BE.json +7 -1
  91. package/src/shared/translations/nl-NL.json +7 -1
  92. package/src/shared/translations/no-NO.json +7 -1
  93. package/src/shared/translations/pl-PL.json +7 -1
  94. package/src/shared/translations/pt-PT.json +7 -1
  95. package/src/shared/translations/sv-SE.json +7 -1
  96. package/src/shared/utils/booking-summary.tsx +46 -0
  97. package/src/shared/utils/localization-util.ts +8 -0
  98. package/src/shared/utils/tide-api-utils.ts +2 -2
  99. package/styles/components/_dropdown.scss +5 -0
  100. package/styles/components/_flyin.scss +43 -0
  101. package/styles/components/_loader.scss +82 -0
  102. package/styles/components/_search.scss +14 -2
  103. package/styles/content-blocks-variables.scss +14 -14
  104. /package/build/build-cjs/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
  105. /package/build/build-esm/src/{booking-wizard/components → shared/booking}/product-card.d.ts +0 -0
  106. /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;