@qite/tide-booking-component 1.1.3 → 1.2.0
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/booking-product/components/multi-range-filter.d.ts +12 -0
- package/build/build-cjs/booking-wizard/features/booking/api.d.ts +4 -1
- package/build/build-cjs/booking-wizard/features/booking/booking-slice.d.ts +7 -3
- package/build/build-cjs/booking-wizard/features/booking/selectors.d.ts +23 -17
- package/build/build-cjs/booking-wizard/features/flight-options/flight-filter.d.ts +9 -0
- package/build/build-cjs/booking-wizard/features/flight-options/flight-option-flight.d.ts +8 -0
- package/build/build-cjs/booking-wizard/features/flight-options/flight-option-modal.d.ts +3 -0
- package/build/build-cjs/booking-wizard/features/flight-options/flight-option.d.ts +4 -9
- package/build/build-cjs/booking-wizard/features/flight-options/flight-utils.d.ts +6 -0
- package/build/build-cjs/booking-wizard/features/room-options/room-utils.d.ts +9 -0
- package/build/build-cjs/booking-wizard/features/room-options/room.d.ts +12 -0
- package/build/build-cjs/booking-wizard/features/room-options/traveler-rooms.d.ts +9 -0
- package/build/build-cjs/booking-wizard/types.d.ts +101 -0
- package/build/build-cjs/index.js +1563 -606
- package/build/build-cjs/shared/utils/localization-util.d.ts +30 -2
- package/build/build-esm/booking-product/components/multi-range-filter.d.ts +12 -0
- package/build/build-esm/booking-wizard/features/booking/api.d.ts +4 -1
- package/build/build-esm/booking-wizard/features/booking/booking-slice.d.ts +7 -3
- package/build/build-esm/booking-wizard/features/booking/selectors.d.ts +23 -17
- package/build/build-esm/booking-wizard/features/flight-options/flight-filter.d.ts +9 -0
- package/build/build-esm/booking-wizard/features/flight-options/flight-option-flight.d.ts +8 -0
- package/build/build-esm/booking-wizard/features/flight-options/flight-option-modal.d.ts +3 -0
- package/build/build-esm/booking-wizard/features/flight-options/flight-option.d.ts +4 -9
- package/build/build-esm/booking-wizard/features/flight-options/flight-utils.d.ts +6 -0
- package/build/build-esm/booking-wizard/features/room-options/room-utils.d.ts +9 -0
- package/build/build-esm/booking-wizard/features/room-options/room.d.ts +12 -0
- package/build/build-esm/booking-wizard/features/room-options/traveler-rooms.d.ts +9 -0
- package/build/build-esm/booking-wizard/types.d.ts +101 -0
- package/build/build-esm/index.js +1564 -607
- package/build/build-esm/shared/utils/localization-util.d.ts +30 -2
- package/package.json +3 -3
- package/rollup.config.js +23 -23
- package/src/booking-product/components/multi-range-filter.css +115 -0
- package/src/booking-product/components/multi-range-filter.tsx +114 -0
- package/src/booking-wizard/components/labeled-input.tsx +64 -64
- package/src/booking-wizard/components/labeled-select.tsx +69 -69
- package/src/booking-wizard/components/step-indicator.tsx +3 -3
- package/src/booking-wizard/features/booking/api.ts +12 -1
- package/src/booking-wizard/features/booking/booking-self-contained.tsx +50 -20
- package/src/booking-wizard/features/booking/booking-slice.ts +45 -9
- package/src/booking-wizard/features/booking/booking.tsx +67 -56
- package/src/booking-wizard/features/booking/selectors.ts +38 -11
- package/src/booking-wizard/features/flight-options/flight-filter.tsx +343 -0
- package/src/booking-wizard/features/flight-options/flight-option-flight.tsx +350 -0
- package/src/booking-wizard/features/flight-options/flight-option.tsx +30 -759
- package/src/booking-wizard/features/flight-options/flight-utils.ts +401 -0
- package/src/booking-wizard/features/flight-options/index.tsx +55 -368
- package/src/booking-wizard/features/price-details/util.ts +6 -6
- package/src/booking-wizard/features/product-options/option-room.tsx +3 -5
- package/src/booking-wizard/features/product-options/options-form.tsx +46 -54
- package/src/booking-wizard/features/room-options/index.tsx +48 -144
- package/src/booking-wizard/features/room-options/room-utils.ts +143 -0
- package/src/booking-wizard/features/room-options/room.tsx +124 -0
- package/src/booking-wizard/features/room-options/traveler-rooms.tsx +63 -0
- package/src/booking-wizard/features/sidebar/sidebar-util.ts +2 -2
- package/src/booking-wizard/features/summary/summary.tsx +2 -2
- package/src/booking-wizard/features/travelers-form/travelers-form.tsx +1 -1
- package/src/booking-wizard/types.ts +116 -0
- package/src/shared/components/rating.tsx +21 -21
- package/src/shared/translations/fr-BE.json +222 -192
- package/src/shared/translations/nl-BE.json +222 -192
- package/src/shared/utils/class-util.ts +9 -9
- package/tsconfig.json +24 -24
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import { getPriceDifferenceText } from "../../../shared/utils/localization-util";
|
|
4
|
+
import { SelectableRoomAccommodation } from "../../types";
|
|
5
|
+
import { selectTranslations } from "../booking/selectors";
|
|
6
|
+
import { getDatePeriodText, getDateText } from "../sidebar/sidebar-util";
|
|
7
|
+
import { buildClassName } from "../../../shared/utils/class-util";
|
|
8
|
+
|
|
9
|
+
interface RoomOptionProps {
|
|
10
|
+
room: SelectableRoomAccommodation;
|
|
11
|
+
hasAlternatives?: boolean;
|
|
12
|
+
selectedRoomPrice?: number;
|
|
13
|
+
onAcommodationChange?: (accommodationCode: string, regimeCode: string) => void,
|
|
14
|
+
onRegimeChange?: (code: string) => void,
|
|
15
|
+
onToggleAlternatives?: () => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const RoomOption: React.FC<RoomOptionProps> = ({
|
|
19
|
+
room,
|
|
20
|
+
hasAlternatives,
|
|
21
|
+
selectedRoomPrice,
|
|
22
|
+
onAcommodationChange,
|
|
23
|
+
onRegimeChange,
|
|
24
|
+
onToggleAlternatives
|
|
25
|
+
}) => {
|
|
26
|
+
const translations = useSelector(selectTranslations);
|
|
27
|
+
|
|
28
|
+
const [roomPrice, setRoomPrice] = useState<number>(0);
|
|
29
|
+
const [roomRegimeCode, setRoomRegimeCode] = useState<string>(room.regimeCode);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
setRoomPrice(room.price);
|
|
33
|
+
}, [room.code, room.regimeCode]);
|
|
34
|
+
|
|
35
|
+
const getPriceDifference = (selectedPrice: number, currentPrice: number) => {
|
|
36
|
+
const priceDifference = (selectedPrice ?? 0) - currentPrice;
|
|
37
|
+
if (priceDifference !== 0) {
|
|
38
|
+
return `(${getPriceDifferenceText(priceDifference)})`;
|
|
39
|
+
} else {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const selectAccommodation = () => {
|
|
45
|
+
if (onAcommodationChange) {
|
|
46
|
+
onAcommodationChange(room.code, roomRegimeCode);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleRegimeChange: React.FormEventHandler<HTMLSelectElement> = (
|
|
51
|
+
e
|
|
52
|
+
) => {
|
|
53
|
+
let regimeCode = e.currentTarget.value;
|
|
54
|
+
|
|
55
|
+
const option = room.regimes.find(x => x.code === regimeCode || (x.code === null && regimeCode === ''))!;
|
|
56
|
+
setRoomRegimeCode(option.code);
|
|
57
|
+
setRoomPrice(option.price);
|
|
58
|
+
|
|
59
|
+
if (onRegimeChange) {
|
|
60
|
+
onRegimeChange(regimeCode);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<div className="form__room">
|
|
69
|
+
{room.viewHtml && (
|
|
70
|
+
<div className="form__room__wrapper" dangerouslySetInnerHTML={{ __html: room.viewHtml }} />
|
|
71
|
+
)}
|
|
72
|
+
{!room.viewHtml && (
|
|
73
|
+
<div className="form__room__wrapper">
|
|
74
|
+
{room.image && (
|
|
75
|
+
<div className="form__room__image">
|
|
76
|
+
<img src={room.image} alt="" className="form__room__img" />
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
<div className="form__room__body">
|
|
80
|
+
<div className="">
|
|
81
|
+
<h3 className="form__room__title">{room.title}</h3>
|
|
82
|
+
{room.description && (
|
|
83
|
+
<p className="form__room__text">{room.description}</p>
|
|
84
|
+
)}
|
|
85
|
+
<ul className="list--usps form__room__usps">
|
|
86
|
+
{room.usps.map((usp, uspIndex) => (
|
|
87
|
+
<li key={uspIndex} className="list__item">{usp}</li>
|
|
88
|
+
))}
|
|
89
|
+
</ul>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
<div className="form__room__footer">
|
|
95
|
+
<div className="form__room__footer__top">
|
|
96
|
+
<p className="form__room__dates">{getDateText(room.from)} - {getDateText(room.to)}</p>
|
|
97
|
+
<span className="form__room__days">{getDatePeriodText(room.from, room.to)}</span>
|
|
98
|
+
</div>
|
|
99
|
+
<div className="form__room__footer__bottom">
|
|
100
|
+
{selectedRoomPrice != undefined && (
|
|
101
|
+
<p className="form__room__price form__room__price--increase"><span>{getPriceDifference(roomPrice, selectedRoomPrice)}</span></p>
|
|
102
|
+
)}
|
|
103
|
+
{hasAlternatives && onToggleAlternatives && (<button type="button" onClick={onToggleAlternatives} className="form__room__alternatives__btn">{translations.ROOM_OPTIONS_FORM.SHOW_ALTERNATIVES}</button>)}
|
|
104
|
+
<div className="select-wrapper">
|
|
105
|
+
<div className="select-wrapper__select">
|
|
106
|
+
<select
|
|
107
|
+
defaultValue={roomRegimeCode}
|
|
108
|
+
onChange={handleRegimeChange}
|
|
109
|
+
>
|
|
110
|
+
{room.regimes.map(regime => (
|
|
111
|
+
<option key={regime.code} value={regime.code ?? ""}>{regime.title} {getPriceDifference(regime.price, roomPrice)}</option>
|
|
112
|
+
))}
|
|
113
|
+
</select>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<button type="button" onClick={selectAccommodation} className={buildClassName(['cta', selectedRoomPrice ? 'cta--secondary' : 'cta--selected'])}>{selectedRoomPrice ? translations.SHARED.SELECT : translations.SHARED.SELECTED}</button>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default RoomOption;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { SelectableRoom } from "../../types";
|
|
3
|
+
import { useSelector } from "react-redux";
|
|
4
|
+
import { selectTranslations } from "../booking/selectors";
|
|
5
|
+
import RoomOption from "./room";
|
|
6
|
+
import { isEmpty } from "lodash";
|
|
7
|
+
|
|
8
|
+
interface TravelerRoomsProps {
|
|
9
|
+
index: number,
|
|
10
|
+
room: SelectableRoom;
|
|
11
|
+
onRoomChange: (index: number, accommodationCode: string, regimeCode: string | null) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const TravelerRooms: React.FC<TravelerRoomsProps> = ({
|
|
15
|
+
index,
|
|
16
|
+
room,
|
|
17
|
+
onRoomChange
|
|
18
|
+
}) => {
|
|
19
|
+
const translations = useSelector(selectTranslations);
|
|
20
|
+
const [showAlternatives, setShowAlternatives] = useState<boolean>(false);
|
|
21
|
+
|
|
22
|
+
const selectedAccommodationCode = room.selected.code;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div key={room.index}>
|
|
26
|
+
<div className="form__room__header">
|
|
27
|
+
<h5>{translations.ROOM_OPTIONS_FORM.TRAVELER_GROUP} {room.index + 1}</h5>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<RoomOption
|
|
31
|
+
room={room.selected}
|
|
32
|
+
hasAlternatives={!isEmpty(room.alternatives)}
|
|
33
|
+
onRegimeChange={(regimeCode) => onRoomChange(index, selectedAccommodationCode, regimeCode)}
|
|
34
|
+
onToggleAlternatives={() => setShowAlternatives(!showAlternatives)} />
|
|
35
|
+
|
|
36
|
+
{!isEmpty(room.alternatives) && showAlternatives && (
|
|
37
|
+
<div className="form__room__alternatives">
|
|
38
|
+
<div className="form__room__alternatives__header">
|
|
39
|
+
{translations.ROOM_OPTIONS_FORM.ALTERNATIVES_TRAVELER_GROUP} {room.index + 1}
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
{showAlternatives && (
|
|
43
|
+
<>
|
|
44
|
+
{room.alternatives.map((alternative, alternativeIndex) => (
|
|
45
|
+
<RoomOption
|
|
46
|
+
key={alternativeIndex}
|
|
47
|
+
room={alternative}
|
|
48
|
+
selectedRoomPrice={room.selected.price}
|
|
49
|
+
onAcommodationChange={(accommodationCode, regimeCode) => {
|
|
50
|
+
setShowAlternatives(false);
|
|
51
|
+
onRoomChange(index, accommodationCode, regimeCode);
|
|
52
|
+
}
|
|
53
|
+
} />
|
|
54
|
+
))}
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default TravelerRooms;
|
|
@@ -154,12 +154,12 @@ export const parseFlightLines = (
|
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
const parseFlightLineDateTime = (
|
|
157
|
-
date:
|
|
157
|
+
date: string,
|
|
158
158
|
time: string
|
|
159
159
|
): string | undefined => {
|
|
160
160
|
try {
|
|
161
161
|
// Parse date
|
|
162
|
-
const parsedDate =
|
|
162
|
+
const parsedDate = parseISO(date);
|
|
163
163
|
|
|
164
164
|
// Parse time
|
|
165
165
|
const parsedTimeHours = parseInt(time.substring(0, 2));
|
|
@@ -202,11 +202,11 @@ const Summary: React.FC<SummaryProps> = () => {
|
|
|
202
202
|
|
|
203
203
|
const result = await validateVoucher(tideClientConfig, request);
|
|
204
204
|
|
|
205
|
-
if (result
|
|
205
|
+
if (result) {
|
|
206
206
|
setVoucher({
|
|
207
207
|
...voucher,
|
|
208
208
|
isValidated: true,
|
|
209
|
-
isValid: result.
|
|
209
|
+
isValid: result.isValid,
|
|
210
210
|
});
|
|
211
211
|
}
|
|
212
212
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BookingPackageFlight } from "@qite/tide-client/build/types";
|
|
2
|
+
|
|
1
3
|
export interface Settings {
|
|
2
4
|
officeId: number;
|
|
3
5
|
bookingOptions: BookingOptions;
|
|
@@ -54,6 +56,9 @@ export interface Settings {
|
|
|
54
56
|
language: string,
|
|
55
57
|
path: string;
|
|
56
58
|
}[];
|
|
59
|
+
accommodations?: AccommodationContent[];
|
|
60
|
+
regimes?: RegimeContent[];
|
|
61
|
+
accommodationViewId?: number;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export interface BookingOptions {
|
|
@@ -152,3 +157,114 @@ export interface FlightLine {
|
|
|
152
157
|
airlineNumber?: string;
|
|
153
158
|
travelClass?: string;
|
|
154
159
|
}
|
|
160
|
+
|
|
161
|
+
export interface AccommodationContent {
|
|
162
|
+
code: string;
|
|
163
|
+
title: string;
|
|
164
|
+
imageUrl: string;
|
|
165
|
+
usps: string[];
|
|
166
|
+
description: string | undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface RegimeContent {
|
|
170
|
+
code: string;
|
|
171
|
+
title: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface SelectableRoom {
|
|
175
|
+
index: number;
|
|
176
|
+
selected: SelectableRoomAccommodation,
|
|
177
|
+
alternatives: SelectableRoomAccommodation[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface SelectableRoomAccommodation {
|
|
181
|
+
code: string;
|
|
182
|
+
regimeCode: string;
|
|
183
|
+
from: string;
|
|
184
|
+
to: string;
|
|
185
|
+
price: number;
|
|
186
|
+
regimes: SelectableRoomRegime[];
|
|
187
|
+
title: string;
|
|
188
|
+
image: string | undefined;
|
|
189
|
+
usps: string[];
|
|
190
|
+
description: string | undefined;
|
|
191
|
+
viewHtml: string | undefined
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface SelectableRoomRegime {
|
|
195
|
+
code: string;
|
|
196
|
+
title: string;
|
|
197
|
+
price: number;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface GroupedFlights {
|
|
201
|
+
isSelected: boolean;
|
|
202
|
+
price: number;
|
|
203
|
+
|
|
204
|
+
outward: GroupedFlightDetails;
|
|
205
|
+
return: GroupedFlightDetails;
|
|
206
|
+
|
|
207
|
+
selectedOutward: BookingPackageFlight;
|
|
208
|
+
selectedReturn: BookingPackageFlight;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface GroupedFlightDetails {
|
|
212
|
+
airlineCode: string;
|
|
213
|
+
airline: string;
|
|
214
|
+
departureDate: string;
|
|
215
|
+
departureTime: string;
|
|
216
|
+
departureAirportCode: string;
|
|
217
|
+
departureAirport: string;
|
|
218
|
+
arrivalDate: string;
|
|
219
|
+
arrivalTime: string;
|
|
220
|
+
arrivalAirport: string;
|
|
221
|
+
changeDurationMinutes: number;
|
|
222
|
+
travelDurationMinutes: number;
|
|
223
|
+
travelDuration: string;
|
|
224
|
+
numberOfStops: number;
|
|
225
|
+
isNextDay: boolean;
|
|
226
|
+
travelClass: string;
|
|
227
|
+
flightLines: GroupedFlightLine[];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface GroupedFlightLine {
|
|
231
|
+
airline: string;
|
|
232
|
+
number: string;
|
|
233
|
+
departureDate: string;
|
|
234
|
+
departureTime: string;
|
|
235
|
+
departureAirport: string;
|
|
236
|
+
arrivalDate: string;
|
|
237
|
+
arrivalTime: string;
|
|
238
|
+
arrivalAirport: string;
|
|
239
|
+
travelDuration: string;
|
|
240
|
+
waitDuration: string | undefined;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface FlightFilterOptions {
|
|
244
|
+
airports: FlightFilterOption[];
|
|
245
|
+
airlines: FlightFilterOption[];
|
|
246
|
+
numberOfStops: FlightFilterOption[];
|
|
247
|
+
outward: FlightDirectionFilter;
|
|
248
|
+
return: FlightDirectionFilter;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface FlightFilterOption {
|
|
252
|
+
value: string;
|
|
253
|
+
label: string;
|
|
254
|
+
count: number;
|
|
255
|
+
isSelected: boolean;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface FlightDirectionFilter {
|
|
259
|
+
departurePeriod: FlightFilterOption[];
|
|
260
|
+
travelDuration: TimeRangeFilter;
|
|
261
|
+
changeDuration: TimeRangeFilter;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface TimeRangeFilter {
|
|
265
|
+
min: number;
|
|
266
|
+
max: number;
|
|
267
|
+
|
|
268
|
+
selectedMin: number;
|
|
269
|
+
selectedMax: number;
|
|
270
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { clamp, range } from "lodash";
|
|
3
|
-
import Icon from "../../booking-wizard/components/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="product-star" key={`rating-star-${i}`} />
|
|
15
|
-
))}
|
|
16
|
-
{clampedRating % 1 > 0 && <Icon name="product-halfstar" />}
|
|
17
|
-
</div>
|
|
18
|
-
);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default Rating;
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { clamp, range } from "lodash";
|
|
3
|
+
import Icon from "../../booking-wizard/components/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="product-star" key={`rating-star-${i}`} />
|
|
15
|
+
))}
|
|
16
|
+
{clampedRating % 1 > 0 && <Icon name="product-halfstar" />}
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default Rating;
|