@qite/tide-booking-component 1.2.4 → 1.2.5

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 (137) hide show
  1. package/.vs/ProjectSettings.json +3 -3
  2. package/.vs/VSWorkspaceState.json +5 -5
  3. package/README.md +8 -8
  4. package/build/build-cjs/booking-wizard/features/flight-options/flight-option-flight.d.ts +1 -1
  5. package/build/build-cjs/booking-wizard/features/flight-options/flight-option.d.ts +1 -1
  6. package/build/build-cjs/booking-wizard/types.d.ts +6 -0
  7. package/build/build-cjs/index.js +1200 -713
  8. package/build/build-cjs/shared/utils/localization-util.d.ts +1 -0
  9. package/build/build-esm/booking-wizard/features/flight-options/flight-option-flight.d.ts +1 -1
  10. package/build/build-esm/booking-wizard/features/flight-options/flight-option.d.ts +1 -1
  11. package/build/build-esm/booking-wizard/types.d.ts +6 -0
  12. package/build/build-esm/index.js +1201 -714
  13. package/build/build-esm/shared/utils/localization-util.d.ts +1 -0
  14. package/package.json +75 -77
  15. package/rollup.config.js +23 -23
  16. package/src/booking-product/components/age-select.tsx +35 -41
  17. package/src/booking-product/components/amount-input.tsx +78 -64
  18. package/src/booking-product/components/date-range-picker/calendar-day.tsx +58 -54
  19. package/src/booking-product/components/date-range-picker/calendar.tsx +178 -176
  20. package/src/booking-product/components/date-range-picker/index.tsx +196 -181
  21. package/src/booking-product/components/dates.tsx +136 -132
  22. package/src/booking-product/components/footer.tsx +69 -70
  23. package/src/booking-product/components/header.tsx +79 -68
  24. package/src/booking-product/components/icon.tsx +251 -208
  25. package/src/booking-product/components/product.tsx +314 -281
  26. package/src/booking-product/components/rating.tsx +21 -21
  27. package/src/booking-product/components/rooms.tsx +195 -180
  28. package/src/booking-product/index.tsx +30 -30
  29. package/src/booking-product/settings-context.ts +14 -14
  30. package/src/booking-product/types.ts +28 -28
  31. package/src/booking-product/utils/api.ts +25 -25
  32. package/src/booking-product/utils/price.ts +29 -27
  33. package/src/booking-wizard/api-settings-slice.ts +24 -24
  34. package/src/booking-wizard/components/icon.tsx +508 -309
  35. package/src/booking-wizard/components/labeled-input.tsx +64 -64
  36. package/src/booking-wizard/components/labeled-select.tsx +69 -69
  37. package/src/booking-wizard/components/message.tsx +34 -34
  38. package/src/booking-wizard/components/multi-range-filter.tsx +113 -113
  39. package/src/booking-wizard/components/product-card.tsx +37 -37
  40. package/src/booking-wizard/components/step-indicator.tsx +51 -51
  41. package/src/booking-wizard/components/step-route.tsx +27 -27
  42. package/src/booking-wizard/declarations.d.ts +4 -4
  43. package/src/booking-wizard/features/booking/api.ts +49 -45
  44. package/src/booking-wizard/features/booking/booking-self-contained.tsx +384 -357
  45. package/src/booking-wizard/features/booking/booking-slice.ts +662 -603
  46. package/src/booking-wizard/features/booking/booking.tsx +356 -349
  47. package/src/booking-wizard/features/booking/constants.ts +16 -16
  48. package/src/booking-wizard/features/booking/selectors.ts +441 -418
  49. package/src/booking-wizard/features/confirmation/confirmation.tsx +97 -94
  50. package/src/booking-wizard/features/error/error.tsx +78 -75
  51. package/src/booking-wizard/features/flight-options/flight-filter.tsx +432 -357
  52. package/src/booking-wizard/features/flight-options/flight-option-flight.tsx +385 -353
  53. package/src/booking-wizard/features/flight-options/flight-option-modal.tsx +229 -214
  54. package/src/booking-wizard/features/flight-options/flight-option.tsx +81 -66
  55. package/src/booking-wizard/features/flight-options/flight-utils.ts +516 -401
  56. package/src/booking-wizard/features/flight-options/index.tsx +196 -177
  57. package/src/booking-wizard/features/price-details/price-details-api.ts +24 -24
  58. package/src/booking-wizard/features/price-details/price-details-slice.ts +178 -171
  59. package/src/booking-wizard/features/price-details/util.ts +155 -155
  60. package/src/booking-wizard/features/product-options/no-options.tsx +21 -21
  61. package/src/booking-wizard/features/product-options/none-option.tsx +120 -120
  62. package/src/booking-wizard/features/product-options/option-booking-airline-group.tsx +64 -66
  63. package/src/booking-wizard/features/product-options/option-booking-group.tsx +216 -210
  64. package/src/booking-wizard/features/product-options/option-item.tsx +317 -318
  65. package/src/booking-wizard/features/product-options/option-pax-card.tsx +201 -188
  66. package/src/booking-wizard/features/product-options/option-pax-group.tsx +175 -169
  67. package/src/booking-wizard/features/product-options/option-room.tsx +321 -314
  68. package/src/booking-wizard/features/product-options/option-unit-group.tsx +198 -192
  69. package/src/booking-wizard/features/product-options/option-units-card.tsx +185 -174
  70. package/src/booking-wizard/features/product-options/options-form.tsx +459 -437
  71. package/src/booking-wizard/features/room-options/index.tsx +187 -172
  72. package/src/booking-wizard/features/room-options/room-utils.ts +190 -143
  73. package/src/booking-wizard/features/room-options/room.tsx +160 -124
  74. package/src/booking-wizard/features/room-options/traveler-rooms.tsx +75 -63
  75. package/src/booking-wizard/features/sidebar/index.tsx +76 -76
  76. package/src/booking-wizard/features/sidebar/sidebar-flight.tsx +68 -68
  77. package/src/booking-wizard/features/sidebar/sidebar-util.ts +177 -177
  78. package/src/booking-wizard/features/sidebar/sidebar.tsx +364 -346
  79. package/src/booking-wizard/features/summary/summary-booking-option-pax.tsx +25 -25
  80. package/src/booking-wizard/features/summary/summary-booking-option-unit.tsx +25 -25
  81. package/src/booking-wizard/features/summary/summary-flight.tsx +39 -39
  82. package/src/booking-wizard/features/summary/summary-per-booking-option-group.tsx +69 -57
  83. package/src/booking-wizard/features/summary/summary-per-pax-option-group.tsx +63 -51
  84. package/src/booking-wizard/features/summary/summary-per-unit-option-group.tsx +66 -54
  85. package/src/booking-wizard/features/summary/summary-slice.ts +28 -28
  86. package/src/booking-wizard/features/summary/summary.tsx +674 -643
  87. package/src/booking-wizard/features/travelers-form/travelers-form-slice.ts +164 -164
  88. package/src/booking-wizard/features/travelers-form/travelers-form.tsx +754 -755
  89. package/src/booking-wizard/features/travelers-form/type-ahead-input.tsx +101 -101
  90. package/src/booking-wizard/features/travelers-form/validate-form.ts +245 -245
  91. package/src/booking-wizard/index.tsx +36 -36
  92. package/src/booking-wizard/settings-context.ts +60 -60
  93. package/src/booking-wizard/store.ts +31 -31
  94. package/src/booking-wizard/types.ts +276 -271
  95. package/src/index.ts +4 -5
  96. package/src/shared/components/loader.tsx +16 -16
  97. package/src/shared/translations/en-GB.json +232 -0
  98. package/src/shared/translations/fr-BE.json +233 -233
  99. package/src/shared/translations/nl-BE.json +232 -232
  100. package/src/shared/types.ts +4 -4
  101. package/src/shared/utils/class-util.ts +9 -9
  102. package/src/shared/utils/localization-util.ts +62 -56
  103. package/src/shared/utils/query-string-util.ts +119 -116
  104. package/src/shared/utils/tide-api-utils.ts +36 -36
  105. package/styles/booking-product-variables.scss +394 -288
  106. package/styles/booking-product.scss +446 -440
  107. package/styles/booking-wizard-variables.scss +871 -530
  108. package/styles/booking-wizard.scss +59 -26
  109. package/styles/components/_animations.scss +39 -39
  110. package/styles/components/_base.scss +107 -106
  111. package/styles/components/_booking.scss +879 -1409
  112. package/styles/components/_button.scss +238 -185
  113. package/styles/components/_checkbox.scss +219 -215
  114. package/styles/components/_cta.scss +208 -133
  115. package/styles/components/_date-list.scss +41 -0
  116. package/styles/components/_date-range-picker.scss +225 -225
  117. package/styles/components/_decrement-increment.scss +35 -37
  118. package/styles/components/_dropdown.scss +72 -74
  119. package/styles/components/_flight-option.scss +1429 -1389
  120. package/styles/components/_form.scss +1583 -394
  121. package/styles/components/_info-message.scss +71 -0
  122. package/styles/components/_input.scss +25 -0
  123. package/styles/components/_list.scss +187 -82
  124. package/styles/components/_loader.scss +72 -71
  125. package/styles/components/_mixins.scss +550 -530
  126. package/styles/components/_placeholders.scss +166 -166
  127. package/styles/components/_pricing-summary.scss +155 -117
  128. package/styles/components/_qsm.scss +17 -20
  129. package/styles/components/_radiobutton.scss +170 -0
  130. package/styles/components/_select-wrapper.scss +80 -66
  131. package/styles/components/_spinner.scss +29 -0
  132. package/styles/components/_step-indicators.scss +168 -160
  133. package/styles/components/_table.scss +81 -81
  134. package/styles/components/_tree.scss +530 -540
  135. package/styles/components/_typeahead.scss +281 -0
  136. package/styles/components/_variables.scss +89 -89
  137. package/tsconfig.json +24 -24
@@ -1,281 +1,314 @@
1
- import JsonURL from "@jsonurl/jsonurl";
2
- import { BookingPackageDetailsRequest, BookingPackagePax, BookingPackageRequest, BookingPackageRequestRoom } from "@qite/tide-client/build/types";
3
- import { addDays, addMonths, format, formatISO } from "date-fns";
4
- import { omit } from "lodash";
5
- import React, { useContext, useEffect, useState } from "react";
6
- import { ApiSettingsState } from "../../shared/types";
7
- import { getTranslations } from "../../shared/utils/localization-util";
8
- import { getDateAsDateFromParams, getRoomsFromParams } from "../../shared/utils/query-string-util";
9
- import SettingsContext from "../settings-context";
10
- import { DateRange, ProductRoom } from "../types";
11
- import packageApi from "../utils/api";
12
- import { formatPriceByMode } from "../utils/price";
13
- import Dates from "./dates";
14
- import Footer from "./footer";
15
- import Header from "./header";
16
- import Rooms from "./rooms";
17
-
18
- interface ProductProps {
19
- productCode: string;
20
- productName: string;
21
- duration?: number;
22
- rating?: number;
23
- }
24
-
25
- const Product: React.FC<ProductProps> = ({
26
- productCode,
27
- productName,
28
- duration,
29
- rating
30
- }) => {
31
- const {
32
- apiKey,
33
- apiUrl,
34
- officeId,
35
- agentId,
36
- catalogueId,
37
- includeFlights,
38
- language,
39
- basePath,
40
- priceMode,
41
- addProductToQuery,
42
- isOffer
43
- } = useContext(SettingsContext);
44
- const translations = getTranslations(language);
45
-
46
- const [loaded, setLoaded] = useState<boolean>(false);
47
- const [isLoading, setIsLoading] = useState<boolean>(false);
48
- const [roomsIsDisabled, setRoomsIsDisabled] = useState<boolean>(true);
49
- const [price, setPrice] = useState<number>();
50
- const [hasFlight, setHasFlight] = useState<boolean>(false);
51
- const [hasTransfer, setHasTransfer] = useState<boolean>(false);
52
- const [rooms, setRooms] = useState<ProductRoom[]>([{ adults: 2, children: 0, childAges: [] }]);
53
- const [dateRange, setDateRange] = useState<DateRange>();
54
- const [packageProductName, setPackageProductName] = useState<string>(productName);
55
-
56
- const fetchPackage = async (signal: AbortSignal) => {
57
- if (
58
- loaded &&
59
- productCode &&
60
- dateRange?.fromDate &&
61
- dateRange?.toDate &&
62
- rooms
63
- ) {
64
-
65
- const apiSettingsState = (apiKey && apiUrl) ? {
66
- apiKey: apiKey,
67
- apiUrl: apiUrl
68
- } as ApiSettingsState : undefined;
69
-
70
- const startDate = formatISO(dateRange?.fromDate, { representation: "date" }) + "T00:00:00Z";
71
- const endDate = formatISO(dateRange?.toDate, { representation: "date" }) + "T00:00:00Z";
72
-
73
- const requestRooms = rooms.map((room, i) => {
74
- const requestRoom = {
75
- index: i,
76
- pax: []
77
- } as BookingPackageRequestRoom;
78
-
79
- for (var a = 0; a < room.adults; a++) {
80
- requestRoom.pax.push({
81
- age: 30,
82
- } as BookingPackagePax);
83
- }
84
-
85
- for (var c = 0; c < room.children; c++) {
86
- requestRoom.pax.push({
87
- age: room.childAges[c],
88
- } as BookingPackagePax);
89
- }
90
-
91
- return requestRoom;
92
- });
93
-
94
- const request = {
95
- officeId: officeId,
96
- agentId: agentId,
97
- payload: {
98
- searchType: 0,
99
- catalogueId: catalogueId,
100
- productCode: productCode,
101
- fromDate: startDate,
102
- toDate: endDate,
103
- includeFlights: includeFlights,
104
- rooms: requestRooms,
105
- } as BookingPackageDetailsRequest,
106
- } as BookingPackageRequest<BookingPackageDetailsRequest>;
107
-
108
- setIsLoading(true);
109
- const response = await packageApi.fetchDetails(request, signal, language, apiSettingsState);
110
- if (!response.errorCode && response.payload) {
111
- const selectedOption = response.payload.options.find(x => x.isSelected);
112
- if (selectedOption) {
113
- const hasFlight = selectedOption.includedServiceTypes.some(x => x === 7);
114
- const hasTranfer = selectedOption.includedServiceTypes.some(x => x === 13);
115
-
116
- setPrice(selectedOption.price);
117
- setHasFlight(hasFlight);
118
- setHasTransfer(hasTranfer);
119
-
120
- if (!productName) {
121
- setPackageProductName(selectedOption.name);
122
- }
123
- }
124
- } else {
125
- setPrice(undefined);
126
- setHasFlight(false);
127
- setHasTransfer(false);
128
- }
129
-
130
- setIsLoading(false);
131
- }
132
- }
133
-
134
- const handleBookClick = () => {
135
- const params: Record<string, string> = {};
136
-
137
- if (rooms) {
138
- const serialized = JsonURL.stringify(
139
- rooms.map((room) => omit(room, ["children"])),
140
- { AQF: true }
141
- );
142
-
143
- if (serialized) {
144
- params["rooms"] = serialized;
145
- }
146
- }
147
-
148
- if (dateRange?.fromDate) {
149
- params["startDate"] = format(dateRange.fromDate, "yyyy-MM-dd");
150
- }
151
-
152
- if (dateRange?.toDate) {
153
- params["endDate"] = format(dateRange.toDate, "yyyy-MM-dd");
154
- }
155
-
156
- params["catalog"] = catalogueId.toString();
157
-
158
- if (addProductToQuery) {
159
- params["productCode"] = productCode;
160
- params["productName"] = encodeURI(packageProductName)!;
161
- }
162
-
163
- const path = window.location.pathname;
164
- const paramString = Object.keys(params)
165
- .map((key) => `${key}=${params[key]}`)
166
- .join("&");
167
-
168
- window.location.href = basePath.startsWith('/')
169
- ? `${window.location.protocol}//${window.location.host}${basePath}?${paramString}`
170
- : `${path}${path.endsWith("/") ? "" : "/"}${basePath}?${paramString}`
171
- }
172
-
173
- const handleRoomChange = (rooms: ProductRoom[]) => {
174
- setRooms(rooms);
175
- }
176
-
177
- const handleDateChange = (value: DateRange) => {
178
- setDateRange(value);
179
- }
180
-
181
- useEffect(() => {
182
- const controller = new AbortController();
183
- const { signal } = controller;
184
-
185
- (async () => {
186
- fetchPackage(signal);
187
- })();
188
- return () => {
189
- controller.abort();
190
- };
191
- }, [
192
- dateRange?.fromDate?.valueOf(),
193
- dateRange?.toDate?.valueOf(),
194
- JSON.stringify(rooms)
195
- ]);
196
-
197
- useEffect(() => {
198
- const params = new URLSearchParams(location.search);
199
- const rooms = getRoomsFromParams(params, "rooms");
200
- const from = getDateAsDateFromParams(params, "from");
201
- const to = getDateAsDateFromParams(params, "to");
202
-
203
- if (rooms) {
204
- setRooms(rooms);
205
- }
206
-
207
- if (from && duration) {
208
- const durationTo = new Date(
209
- Date.UTC(from.getFullYear(), from.getMonth(), from.getDate() + duration)
210
- );
211
-
212
- setDateRange({ fromDate: from, toDate: durationTo });
213
- } else if (from && to) {
214
- setDateRange({ fromDate: from, toDate: to });
215
- } else if (duration) {
216
- const now = new Date();
217
-
218
- const durationFrom = new Date(Date.UTC(now.getFullYear(), now.getMonth() + 3, now.getDate()));
219
- const durationTo = new Date(
220
- Date.UTC(
221
- durationFrom.getFullYear(),
222
- durationFrom.getMonth(),
223
- durationFrom.getDate() + duration
224
- )
225
- );
226
-
227
- setDateRange({ fromDate: durationFrom, toDate: durationTo });
228
- } else {
229
- const now = new Date();
230
- const startDate = addMonths(now, 1);
231
- const endDate = addDays(startDate, 7);
232
-
233
- setDateRange({ fromDate: startDate, toDate: endDate });
234
- }
235
-
236
- setLoaded(true);
237
- }, [location]);
238
-
239
- const personCount = rooms.reduce((s, r) => s + r.adults + r.children, 0);
240
- const durationCount = 0;
241
- const priceText = formatPriceByMode(
242
- price,
243
- priceMode,
244
- personCount,
245
- durationCount,
246
- translations.PRODUCT.PER_PERSON,
247
- translations.PRODUCT.PER_NIGHT,
248
- translations.PRODUCT.PER_PERSON_PER_NIGHT
249
- );
250
-
251
- return (
252
- <div className="booking-product">
253
- <Header
254
- name={packageProductName}
255
- rating={rating}
256
- priceText={priceText}
257
- isLoading={isLoading}
258
- hasFlight={hasFlight}
259
- hasTransfer={hasTransfer} />
260
- <div className="booking-product__body">
261
- <Rooms
262
- rooms={rooms}
263
- isDisabled={roomsIsDisabled}
264
- setIsDisabled={setRoomsIsDisabled}
265
- onChange={handleRoomChange} />
266
- <Dates
267
- value={dateRange}
268
- duration={duration}
269
- onChange={handleDateChange} />
270
- </div>
271
- <Footer
272
- priceText={priceText}
273
- isLoading={isLoading}
274
- isOffer={isOffer}
275
- roomsIsDisabled={roomsIsDisabled}
276
- handleBookClick={handleBookClick} />
277
- </div>
278
- );
279
- }
280
-
281
- export default Product;
1
+ import JsonURL from "@jsonurl/jsonurl";
2
+ import {
3
+ BookingPackageDetailsRequest,
4
+ BookingPackagePax,
5
+ BookingPackageRequest,
6
+ BookingPackageRequestRoom,
7
+ } from "@qite/tide-client/build/types";
8
+ import { addDays, addMonths, format, formatISO } from "date-fns";
9
+ import { omit } from "lodash";
10
+ import React, { useContext, useEffect, useState } from "react";
11
+ import { ApiSettingsState } from "../../shared/types";
12
+ import { getTranslations } from "../../shared/utils/localization-util";
13
+ import {
14
+ getDateAsDateFromParams,
15
+ getRoomsFromParams,
16
+ } from "../../shared/utils/query-string-util";
17
+ import SettingsContext from "../settings-context";
18
+ import { DateRange, ProductRoom } from "../types";
19
+ import packageApi from "../utils/api";
20
+ import { formatPriceByMode } from "../utils/price";
21
+ import Dates from "./dates";
22
+ import Footer from "./footer";
23
+ import Header from "./header";
24
+ import Rooms from "./rooms";
25
+
26
+ interface ProductProps {
27
+ productCode: string;
28
+ productName: string;
29
+ duration?: number;
30
+ rating?: number;
31
+ }
32
+
33
+ const Product: React.FC<ProductProps> = ({
34
+ productCode,
35
+ productName,
36
+ duration,
37
+ rating,
38
+ }) => {
39
+ const {
40
+ apiKey,
41
+ apiUrl,
42
+ officeId,
43
+ agentId,
44
+ catalogueId,
45
+ includeFlights,
46
+ language,
47
+ basePath,
48
+ priceMode,
49
+ addProductToQuery,
50
+ isOffer,
51
+ } = useContext(SettingsContext);
52
+ const translations = getTranslations(language);
53
+
54
+ const [loaded, setLoaded] = useState<boolean>(false);
55
+ const [isLoading, setIsLoading] = useState<boolean>(false);
56
+ const [roomsIsDisabled, setRoomsIsDisabled] = useState<boolean>(true);
57
+ const [price, setPrice] = useState<number>();
58
+ const [hasFlight, setHasFlight] = useState<boolean>(false);
59
+ const [hasTransfer, setHasTransfer] = useState<boolean>(false);
60
+ const [rooms, setRooms] = useState<ProductRoom[]>([
61
+ { adults: 2, children: 0, childAges: [] },
62
+ ]);
63
+ const [dateRange, setDateRange] = useState<DateRange>();
64
+ const [packageProductName, setPackageProductName] =
65
+ useState<string>(productName);
66
+
67
+ const fetchPackage = async (signal: AbortSignal) => {
68
+ if (
69
+ loaded &&
70
+ productCode &&
71
+ dateRange?.fromDate &&
72
+ dateRange?.toDate &&
73
+ rooms
74
+ ) {
75
+ const apiSettingsState =
76
+ apiKey && apiUrl
77
+ ? ({
78
+ apiKey: apiKey,
79
+ apiUrl: apiUrl,
80
+ } as ApiSettingsState)
81
+ : undefined;
82
+
83
+ const startDate =
84
+ formatISO(dateRange?.fromDate, { representation: "date" }) +
85
+ "T00:00:00Z";
86
+ const endDate =
87
+ formatISO(dateRange?.toDate, { representation: "date" }) + "T00:00:00Z";
88
+
89
+ const requestRooms = rooms.map((room, i) => {
90
+ const requestRoom = {
91
+ index: i,
92
+ pax: [],
93
+ } as BookingPackageRequestRoom;
94
+
95
+ for (var a = 0; a < room.adults; a++) {
96
+ requestRoom.pax.push({
97
+ age: 30,
98
+ } as BookingPackagePax);
99
+ }
100
+
101
+ for (var c = 0; c < room.children; c++) {
102
+ requestRoom.pax.push({
103
+ age: room.childAges[c],
104
+ } as BookingPackagePax);
105
+ }
106
+
107
+ return requestRoom;
108
+ });
109
+
110
+ const request = {
111
+ officeId: officeId,
112
+ agentId: agentId,
113
+ payload: {
114
+ searchType: 0,
115
+ catalogueId: catalogueId,
116
+ productCode: productCode,
117
+ fromDate: startDate,
118
+ toDate: endDate,
119
+ includeFlights: includeFlights,
120
+ rooms: requestRooms,
121
+ } as BookingPackageDetailsRequest,
122
+ } as BookingPackageRequest<BookingPackageDetailsRequest>;
123
+
124
+ setIsLoading(true);
125
+ const response = await packageApi.fetchDetails(
126
+ request,
127
+ signal,
128
+ language,
129
+ apiSettingsState
130
+ );
131
+ if (!response.errorCode && response.payload) {
132
+ const selectedOption = response.payload.options.find(
133
+ (x) => x.isSelected
134
+ );
135
+ if (selectedOption) {
136
+ const hasFlight = selectedOption.includedServiceTypes.some(
137
+ (x) => x === 7
138
+ );
139
+ const hasTranfer = selectedOption.includedServiceTypes.some(
140
+ (x) => x === 13
141
+ );
142
+
143
+ setPrice(selectedOption.price);
144
+ setHasFlight(hasFlight);
145
+ setHasTransfer(hasTranfer);
146
+
147
+ if (!productName) {
148
+ setPackageProductName(selectedOption.name);
149
+ }
150
+ }
151
+ } else {
152
+ setPrice(undefined);
153
+ setHasFlight(false);
154
+ setHasTransfer(false);
155
+ }
156
+
157
+ setIsLoading(false);
158
+ }
159
+ };
160
+
161
+ const handleBookClick = () => {
162
+ const params: Record<string, string> = {};
163
+
164
+ if (rooms) {
165
+ const serialized = JsonURL.stringify(
166
+ rooms.map((room) => omit(room, ["children"])),
167
+ { AQF: true }
168
+ );
169
+
170
+ if (serialized) {
171
+ params["rooms"] = serialized;
172
+ }
173
+ }
174
+
175
+ if (dateRange?.fromDate) {
176
+ params["startDate"] = format(dateRange.fromDate, "yyyy-MM-dd");
177
+ }
178
+
179
+ if (dateRange?.toDate) {
180
+ params["endDate"] = format(dateRange.toDate, "yyyy-MM-dd");
181
+ }
182
+
183
+ params["catalog"] = catalogueId.toString();
184
+
185
+ if (addProductToQuery) {
186
+ params["productCode"] = productCode;
187
+ params["productName"] = encodeURI(packageProductName)!;
188
+ }
189
+
190
+ const path = window.location.pathname;
191
+ const paramString = Object.keys(params)
192
+ .map((key) => `${key}=${params[key]}`)
193
+ .join("&");
194
+
195
+ window.location.href = basePath.startsWith("/")
196
+ ? `${window.location.protocol}//${window.location.host}${basePath}?${paramString}`
197
+ : `${path}${path.endsWith("/") ? "" : "/"}${basePath}?${paramString}`;
198
+ };
199
+
200
+ const handleRoomChange = (rooms: ProductRoom[]) => {
201
+ setRooms(rooms);
202
+ };
203
+
204
+ const handleDateChange = (value: DateRange) => {
205
+ setDateRange(value);
206
+ };
207
+
208
+ useEffect(() => {
209
+ const controller = new AbortController();
210
+ const { signal } = controller;
211
+
212
+ (async () => {
213
+ fetchPackage(signal);
214
+ })();
215
+ return () => {
216
+ controller.abort();
217
+ };
218
+ }, [
219
+ dateRange?.fromDate?.valueOf(),
220
+ dateRange?.toDate?.valueOf(),
221
+ JSON.stringify(rooms),
222
+ ]);
223
+
224
+ useEffect(() => {
225
+ const params = new URLSearchParams(location.search);
226
+ const rooms = getRoomsFromParams(params, "rooms");
227
+ const from = getDateAsDateFromParams(params, "from");
228
+ const to = getDateAsDateFromParams(params, "to");
229
+
230
+ if (rooms) {
231
+ setRooms(rooms);
232
+ }
233
+
234
+ if (from && duration) {
235
+ const durationTo = new Date(
236
+ Date.UTC(from.getFullYear(), from.getMonth(), from.getDate() + duration)
237
+ );
238
+
239
+ setDateRange({ fromDate: from, toDate: durationTo });
240
+ } else if (from && to) {
241
+ setDateRange({ fromDate: from, toDate: to });
242
+ } else if (duration) {
243
+ const now = new Date();
244
+
245
+ const durationFrom = new Date(
246
+ Date.UTC(now.getFullYear(), now.getMonth() + 3, now.getDate())
247
+ );
248
+ const durationTo = new Date(
249
+ Date.UTC(
250
+ durationFrom.getFullYear(),
251
+ durationFrom.getMonth(),
252
+ durationFrom.getDate() + duration
253
+ )
254
+ );
255
+
256
+ setDateRange({ fromDate: durationFrom, toDate: durationTo });
257
+ } else {
258
+ const now = new Date();
259
+ const startDate = addMonths(now, 1);
260
+ const endDate = addDays(startDate, 7);
261
+
262
+ setDateRange({ fromDate: startDate, toDate: endDate });
263
+ }
264
+
265
+ setLoaded(true);
266
+ }, [location]);
267
+
268
+ const personCount = rooms.reduce((s, r) => s + r.adults + r.children, 0);
269
+ const durationCount = 0;
270
+ const priceText = formatPriceByMode(
271
+ price,
272
+ priceMode,
273
+ personCount,
274
+ durationCount,
275
+ translations.PRODUCT.PER_PERSON,
276
+ translations.PRODUCT.PER_NIGHT,
277
+ translations.PRODUCT.PER_PERSON_PER_NIGHT
278
+ );
279
+
280
+ return (
281
+ <div className="booking-product">
282
+ <Header
283
+ name={packageProductName}
284
+ rating={rating}
285
+ priceText={priceText}
286
+ isLoading={isLoading}
287
+ hasFlight={hasFlight}
288
+ hasTransfer={hasTransfer}
289
+ />
290
+ <div className="booking-product__body">
291
+ <Rooms
292
+ rooms={rooms}
293
+ isDisabled={roomsIsDisabled}
294
+ setIsDisabled={setRoomsIsDisabled}
295
+ onChange={handleRoomChange}
296
+ />
297
+ <Dates
298
+ value={dateRange}
299
+ duration={duration}
300
+ onChange={handleDateChange}
301
+ />
302
+ </div>
303
+ <Footer
304
+ priceText={priceText}
305
+ isLoading={isLoading}
306
+ isOffer={isOffer}
307
+ roomsIsDisabled={roomsIsDisabled}
308
+ handleBookClick={handleBookClick}
309
+ />
310
+ </div>
311
+ );
312
+ };
313
+
314
+ export default Product;
@@ -1,21 +1,21 @@
1
- import React from "react";
2
- import { clamp, range } from "lodash";
3
- import Icon from "./icon";
4
-
5
- interface RatingProps {
6
- rating: number;
7
- }
8
-
9
- const Rating: React.FC<RatingProps> = ({ rating }) => {
10
- const clampedRating = clamp(rating, 0, 5);
11
- return (
12
- <div className="rating">
13
- {range(0, Math.floor(clampedRating)).map((i) => (
14
- <Icon name="ui-star" key={`rating-star-${i}`} width={25} height={25} />
15
- ))}
16
- {rating % 1 > 0 && <Icon name="ui-halfstar" width={25} height={25} />}
17
- </div>
18
- );
19
- };
20
-
21
- export default Rating;
1
+ import React from "react";
2
+ import { clamp, range } from "lodash";
3
+ import Icon from "./icon";
4
+
5
+ interface RatingProps {
6
+ rating: number;
7
+ }
8
+
9
+ const Rating: React.FC<RatingProps> = ({ rating }) => {
10
+ const clampedRating = clamp(rating, 0, 5);
11
+ return (
12
+ <div className="rating">
13
+ {range(0, Math.floor(clampedRating)).map((i) => (
14
+ <Icon name="ui-star" key={`rating-star-${i}`} width={25} height={25} />
15
+ ))}
16
+ {rating % 1 > 0 && <Icon name="ui-halfstar" width={25} height={25} />}
17
+ </div>
18
+ );
19
+ };
20
+
21
+ export default Rating;