kupos-ui-components-lib 9.1.8 → 9.1.10
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/dist/components/ServiceItem/ServiceItemDesktop.js +2 -3
- package/dist/components/ServiceItem/ServiceItemMobile.js +25 -24
- package/dist/styles.css +13 -0
- package/dist/ui/SeatSection/SeatSection.d.ts +2 -1
- package/dist/ui/SeatSection/SeatSection.js +28 -12
- package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +2 -1
- package/dist/ui/mobileweb/DateTimeSectionMobile.js +2 -2
- package/dist/ui/mobileweb/SeatSectionMobile.d.ts +2 -1
- package/dist/ui/mobileweb/SeatSectionMobile.js +17 -9
- package/dist/utils/CommonService.d.ts +8 -0
- package/dist/utils/CommonService.js +17 -0
- package/package.json +1 -1
- package/src/components/ServiceItem/ServiceItemDesktop.tsx +2 -2
- package/src/components/ServiceItem/ServiceItemMobile.tsx +36 -28
- package/src/ui/SeatSection/SeatSection.tsx +83 -35
- package/src/ui/mobileweb/DateTimeSectionMobile.tsx +3 -0
- package/src/ui/mobileweb/SeatSectionMobile.tsx +48 -19
- package/src/utils/CommonService.ts +34 -0
|
@@ -217,8 +217,7 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
|
|
|
217
217
|
const id = setInterval(() => {
|
|
218
218
|
remaining -= 1;
|
|
219
219
|
if (remaining <= 0) {
|
|
220
|
-
remaining =
|
|
221
|
-
clearInterval(id);
|
|
220
|
+
remaining = countdownSeconds;
|
|
222
221
|
}
|
|
223
222
|
node.textContent = formatTime(remaining);
|
|
224
223
|
}, 1000);
|
|
@@ -303,7 +302,7 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
|
|
|
303
302
|
backgroundColor: "#ccc",
|
|
304
303
|
} }),
|
|
305
304
|
React.createElement("div", { className: "content-center" },
|
|
306
|
-
React.createElement(SeatSection, { seatTypes: serviceItem.seat_types, availableSeats: serviceItem.available_seats, isSoldOut: isSoldOut, priceColor: colors.priceColor, currencySign: currencySign, removeDuplicateSeats: removeDuplicateSeats, isPeru: isPeru })),
|
|
305
|
+
React.createElement(SeatSection, { seatTypes: serviceItem.seat_types, serviceItem: serviceItem, availableSeats: serviceItem.available_seats, isSoldOut: isSoldOut, priceColor: colors.priceColor, currencySign: currencySign, removeDuplicateSeats: removeDuplicateSeats, isPeru: isPeru })),
|
|
307
306
|
React.createElement("div", null,
|
|
308
307
|
showLastSeats ? (React.createElement("div", { className: "flex justify-end mr-[11px] ", style: {
|
|
309
308
|
position: "absolute",
|
|
@@ -17,10 +17,11 @@ const exceptions = [
|
|
|
17
17
|
"asiento_mascota",
|
|
18
18
|
];
|
|
19
19
|
function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, }) {
|
|
20
|
-
var _a, _b, _c;
|
|
20
|
+
var _a, _b, _c, _d;
|
|
21
21
|
const isItemExpanded = serviceItem.id === isExpanded;
|
|
22
22
|
const isPetSeat = (Object.keys(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.pet_seat_info) || []).length > 0;
|
|
23
23
|
let isSoldOut = serviceItem.available_seats <= 0;
|
|
24
|
+
const isLongOfferText = (((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) === null || _a === void 0 ? void 0 : _a.length) || 0) > 35;
|
|
24
25
|
const startViewerCount = (node) => {
|
|
25
26
|
if (!node || !viewersConfig)
|
|
26
27
|
return;
|
|
@@ -56,8 +57,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
|
|
|
56
57
|
const id = setInterval(() => {
|
|
57
58
|
remaining -= 1;
|
|
58
59
|
if (remaining <= 0) {
|
|
59
|
-
remaining =
|
|
60
|
-
clearInterval(id);
|
|
60
|
+
remaining = countdownSeconds;
|
|
61
61
|
}
|
|
62
62
|
node.textContent = formatTime(remaining);
|
|
63
63
|
}, 1000);
|
|
@@ -114,7 +114,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
|
|
|
114
114
|
// className={`relative ${
|
|
115
115
|
// serviceItem.offer_text ? "mb-[55px]" : "mb-[14px]"
|
|
116
116
|
// }
|
|
117
|
-
className: `relative ${serviceItem.offer_text ? "mb-[
|
|
117
|
+
className: `relative ${!serviceItem.offer_text ? "mb-[14px]" : showTopLabel || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ? "mb-[20px]" : "mb-[12px]"} ${(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ||
|
|
118
118
|
isConexion ||
|
|
119
119
|
(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.train_type_label) === "Tren Express (Nuevo)" ||
|
|
120
120
|
showTopLabel
|
|
@@ -141,9 +141,9 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
|
|
|
141
141
|
React.createElement("span", { className: "ml-[3px] min-[420]:text-[13px] text-[12px] text-ellipsis" }, serviceItem.operator_details[2]))))) : null),
|
|
142
142
|
showLastSeats ? (React.createElement("div", { className: "flex justify-end " }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) < 10 &&
|
|
143
143
|
(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) > 0 && (React.createElement("div", { className: "text-[10px] text-[#464647] text-center" }, "\u00A1 \u00DAltimos Asientos!")))) : null),
|
|
144
|
-
React.createElement(DateTimeSectionMobile, { onBookButtonPress: onBookButtonPress, isCiva: isCiva, isSoldOut: isSoldOut, isLinatal: isLinatal, isPeru: isPeru, orignLabel: orignLabel, destinationLabel: destinationLabel, originIcon: (
|
|
144
|
+
React.createElement(DateTimeSectionMobile, { onBookButtonPress: onBookButtonPress, isCiva: isCiva, isSoldOut: isSoldOut, isLinatal: isLinatal, isPeru: isPeru, orignLabel: orignLabel, destinationLabel: destinationLabel, originIcon: (_b = serviceItem.icons) === null || _b === void 0 ? void 0 : _b.origin, destinationIcon: (_c = serviceItem.icons) === null || _c === void 0 ? void 0 : _c.destination, travelDate: serviceItem.travel_date, arrivalDate: serviceItem.arrival_date, depTime: serviceItem.dep_time, arrTime: serviceItem.arr_time, seatTypes: serviceItem.seat_types, seatPriceColor: colors.seatPriceColor, currencySign: currencySign, availableSeats: serviceItem.available_seats, removeDuplicateSeats: removeDuplicateSeats, serviceItem: serviceItem }),
|
|
145
145
|
React.createElement("div", { className: "bg-[#E6E6E6] mt-[10px] mb-[10px] h-[1px]" }),
|
|
146
|
-
React.createElement(BottomAmenitiesMobile, { isSoldOut: isSoldOut, amenitiesNodes: amenities(), hoursIcon: renderIcon("hours", "14px"), duration: (
|
|
146
|
+
React.createElement(BottomAmenitiesMobile, { isSoldOut: isSoldOut, amenitiesNodes: amenities(), hoursIcon: renderIcon("hours", "14px"), duration: (_d = serviceItem.duration) === null || _d === void 0 ? void 0 : _d.toString(), isDirectTrip: serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip, directoColor: colors.directoColor, directoAnim: serviceItem.icons.directoAnim, isChangeTicket: serviceItem.is_change_ticket, isPetSeat: isPetSeat, petSeatInfo: serviceItem.pet_seat_info, petFriendlyAnim: serviceItem.icons.petFriendlyAnim, flexibleAnim: serviceItem.icons.flexibleAnim, isTrackingEnabled: serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_tracking_enabled, locationAnim: serviceItem.icons.locationAnim, downArrowIcon: serviceItem.icons.downArrow, showDropdown: isItemExpanded, setShowDropdown: () => setIsExpanded(isItemExpanded ? null : serviceItem.id),
|
|
147
147
|
// onDropdownToggle={() => {
|
|
148
148
|
// setShowDropdown(!showDropdown);
|
|
149
149
|
// setAmenetiesAtomValue({
|
|
@@ -177,35 +177,36 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
|
|
|
177
177
|
} },
|
|
178
178
|
React.createElement(LottiePlayer, { animationData: serviceItem.icons.directoAnim, width: "20px", height: "20px" }),
|
|
179
179
|
React.createElement("div", null, "Tren Express"))))),
|
|
180
|
-
(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[
|
|
180
|
+
(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]", style: {
|
|
181
181
|
backgroundColor: isSoldOut ? "#ccc" : colors === null || colors === void 0 ? void 0 : colors.bottomStripColor,
|
|
182
182
|
opacity: isSoldOut ? 0.5 : 1,
|
|
183
183
|
borderRadius: "0 0 14px 14px",
|
|
184
184
|
zIndex: -1,
|
|
185
185
|
} },
|
|
186
186
|
React.createElement("div", { className: "flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]", style: { lineHeight: 1.6 } },
|
|
187
|
-
React.createElement("div", { className: "flex flex-col" },
|
|
188
|
-
React.createElement("div", { className:
|
|
189
|
-
React.createElement(
|
|
190
|
-
|
|
187
|
+
React.createElement("div", { className: "flex flex-col gap-[4px]" },
|
|
188
|
+
React.createElement("div", { className: `flex ${isLongOfferText ? "items-start" : "items-center"}` },
|
|
189
|
+
React.createElement("div", { className: isLongOfferText ? "mt-[2px]" : "" },
|
|
190
|
+
React.createElement(LottiePlayer, { animationData: serviceItem.icons.bombAnim, width: "18px", height: "18px" })),
|
|
191
|
+
React.createElement("div", { className: `ml-[6px] flex-1 outline-none ${isLongOfferText ? "mt-[2px]" : ""}`, style: {
|
|
191
192
|
color: "#fff",
|
|
193
|
+
lineHeight: 1.4,
|
|
192
194
|
} },
|
|
193
|
-
React.createElement("span", { className: "
|
|
194
|
-
(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || "",
|
|
195
|
-
"\u00A0"),
|
|
195
|
+
React.createElement("span", { className: "min-[380px]:text-[12px] bold-text" }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || ""),
|
|
196
196
|
" ",
|
|
197
|
-
"
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
React.createElement("span", { className: "min-[380px]:text-[12px]" }, "|"),
|
|
198
|
+
" ",
|
|
199
|
+
React.createElement("span", { className: "whitespace-nowrap min-[380px]:text-[12px]" },
|
|
200
|
+
"Termina en\u00A0",
|
|
201
|
+
React.createElement("span", { className: "bold-text", ref: startCountdown, style: {
|
|
202
|
+
fontVariantNumeric: "tabular-nums",
|
|
203
|
+
display: "inline-block",
|
|
204
|
+
} })))),
|
|
205
|
+
React.createElement("div", { className: "flex items-start", style: {
|
|
204
206
|
color: "#fff",
|
|
205
207
|
} },
|
|
206
|
-
renderIcon("personIcon", "16px"),
|
|
207
|
-
"
|
|
208
|
-
React.createElement("span", { className: "" },
|
|
208
|
+
React.createElement("div", null, renderIcon("personIcon", "16px")),
|
|
209
|
+
React.createElement("span", { className: "flex-1", style: { lineHeight: 1.4 } },
|
|
209
210
|
React.createElement("span", { className: "bold-text", ref: startViewerCount, style: { fontVariantNumeric: "tabular-nums" } }),
|
|
210
211
|
" ",
|
|
211
212
|
React.createElement("span", { className: "bold-text" }, "personas"),
|
package/dist/styles.css
CHANGED
|
@@ -201,6 +201,9 @@
|
|
|
201
201
|
.mb-\[10px\] {
|
|
202
202
|
margin-bottom: 10px;
|
|
203
203
|
}
|
|
204
|
+
.mb-\[12px\] {
|
|
205
|
+
margin-bottom: 12px;
|
|
206
|
+
}
|
|
204
207
|
.mb-\[14px\] {
|
|
205
208
|
margin-bottom: 14px;
|
|
206
209
|
}
|
|
@@ -457,6 +460,9 @@
|
|
|
457
460
|
.items-center {
|
|
458
461
|
align-items: center;
|
|
459
462
|
}
|
|
463
|
+
.items-start {
|
|
464
|
+
align-items: flex-start;
|
|
465
|
+
}
|
|
460
466
|
.justify-between {
|
|
461
467
|
justify-content: space-between;
|
|
462
468
|
}
|
|
@@ -738,6 +744,9 @@
|
|
|
738
744
|
.pb-\[7px\] {
|
|
739
745
|
padding-bottom: 7px;
|
|
740
746
|
}
|
|
747
|
+
.pb-\[8px\] {
|
|
748
|
+
padding-bottom: 8px;
|
|
749
|
+
}
|
|
741
750
|
.pb-\[10px\] {
|
|
742
751
|
padding-bottom: 10px;
|
|
743
752
|
}
|
|
@@ -892,6 +901,10 @@
|
|
|
892
901
|
--tw-duration: 300ms;
|
|
893
902
|
transition-duration: 300ms;
|
|
894
903
|
}
|
|
904
|
+
.outline-none {
|
|
905
|
+
--tw-outline-style: none;
|
|
906
|
+
outline-style: none;
|
|
907
|
+
}
|
|
895
908
|
.group-hover\:block {
|
|
896
909
|
&:is(:where(.group):hover *) {
|
|
897
910
|
@media (hover: hover) {
|
|
@@ -12,6 +12,7 @@ interface SeatSectionProps {
|
|
|
12
12
|
currencySign?: string;
|
|
13
13
|
removeDuplicateSeats?: boolean;
|
|
14
14
|
isPeru?: boolean;
|
|
15
|
+
serviceItem?: any;
|
|
15
16
|
}
|
|
16
|
-
declare function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, isPeru, }: SeatSectionProps): React.ReactElement;
|
|
17
|
+
declare function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, isPeru, serviceItem, }: SeatSectionProps): React.ReactElement;
|
|
17
18
|
export default SeatSection;
|
|
@@ -54,7 +54,7 @@ function getUniqueSeats(seatTypes) {
|
|
|
54
54
|
function getNumberOfSeats(seatTypes) {
|
|
55
55
|
return seatTypes.filter((val) => !SEAT_EXCEPTIONS.includes(val.label)).length;
|
|
56
56
|
}
|
|
57
|
-
function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, isPeru, }) {
|
|
57
|
+
function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, isPeru, serviceItem, }) {
|
|
58
58
|
const uniqueSeats = getUniqueSeats(seatTypes);
|
|
59
59
|
const sortedSeatTypes = getSortedSeatTypes(seatTypes);
|
|
60
60
|
const numberOfSeats = getNumberOfSeats(seatTypes);
|
|
@@ -74,30 +74,46 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
|
|
|
74
74
|
if (isPeru) {
|
|
75
75
|
const allSeats = getAllSeatTypes(seatTypes);
|
|
76
76
|
const lowestFare = allSeats.length > 0 ? allSeats[0].price : 0;
|
|
77
|
-
|
|
77
|
+
const { originalPrice, discountedPrice } = CommonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
78
|
+
return (React.createElement(React.Fragment, null,
|
|
79
|
+
originalPrice !== discountedPrice && (React.createElement("span", { className: "text-[13.33px]", style: strikethroughStyle }, formatPrice(originalPrice))),
|
|
80
|
+
React.createElement("span", { className: "flex items-center text-[13.33px] bold-text" }, formatPrice(discountedPrice))));
|
|
78
81
|
}
|
|
79
82
|
if (removeDuplicateSeats) {
|
|
80
|
-
return uniqueSeats.map((val, key) =>
|
|
83
|
+
return uniqueSeats.map((val, key) => {
|
|
84
|
+
const { discountedPrice } = CommonService.calculateDiscountedPrice(val.price, serviceItem);
|
|
85
|
+
return React.createElement("span", { key: key }, formatPrice(discountedPrice));
|
|
86
|
+
});
|
|
81
87
|
}
|
|
82
|
-
return sortedSeatTypes.map((val, key) =>
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
return sortedSeatTypes.map((val, key) => {
|
|
89
|
+
if (SEAT_EXCEPTIONS.includes(val.label))
|
|
90
|
+
return null;
|
|
91
|
+
const { discountedPrice } = CommonService.calculateDiscountedPrice(val.price, serviceItem);
|
|
92
|
+
return (React.createElement("span", { key: key, className: "flex items-center text-[13.33px] bold-text" }, typeof discountedPrice === "string" ||
|
|
93
|
+
typeof discountedPrice === "number"
|
|
94
|
+
? formatPrice(discountedPrice)
|
|
95
|
+
: null));
|
|
96
|
+
});
|
|
85
97
|
};
|
|
98
|
+
const strikethroughStyle = Object.assign({ color: "#ccc", display: "flex", textAlign: "end", textDecoration: "line-through" }, (isPeru
|
|
99
|
+
? { position: "relative", top: 0 }
|
|
100
|
+
: { position: "absolute", top: isCentered ? "-10px" : "-18px" }));
|
|
101
|
+
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
102
|
+
const discountedSeats = seats.map((seat) => (Object.assign(Object.assign({}, seat), CommonService.calculateDiscountedPrice(seat.price, serviceItem))));
|
|
103
|
+
const highestOriginalPrice = Math.max(...discountedSeats.map((seat) => seat.originalPrice));
|
|
104
|
+
const hasDiscount = discountedSeats.some((seat) => seat.originalPrice !== seat.discountedPrice);
|
|
86
105
|
const renderLabels = () => {
|
|
87
106
|
if (isPeru) {
|
|
88
107
|
return (React.createElement(React.Fragment, null,
|
|
89
|
-
React.createElement("span", { className: "text-[13.33px]", style: {
|
|
108
|
+
hasDiscount && (React.createElement("span", { className: "text-[13.33px]", style: {
|
|
90
109
|
color: "#999",
|
|
91
110
|
// position: "relative",
|
|
92
111
|
// bottom: numberOfSeats ? "10px" : "",
|
|
93
|
-
} }, "Antes"),
|
|
112
|
+
} }, "Antes")),
|
|
94
113
|
React.createElement("span", { className: "text-[13.33px]" }, "Desde")));
|
|
95
114
|
}
|
|
96
115
|
return renderSeatNames();
|
|
97
116
|
};
|
|
98
|
-
const strikethroughStyle = Object.assign({ color: "#ccc", display: "flex", textAlign: "end", textDecoration: "line-through" }, (isPeru
|
|
99
|
-
? { position: "relative", top: 0 }
|
|
100
|
-
: { position: "absolute", top: isCentered ? "-10px" : "-18px" }));
|
|
101
117
|
return (React.createElement("div", { className: "relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]", style: isCentered ? { alignItems: "center" } : {} },
|
|
102
118
|
React.createElement("div", { className: "flex flex-col justify-between", style: { gap: "10px" } }, renderLabels()),
|
|
103
119
|
React.createElement("div", { className: "flex flex-col justify-between absolute inset-y-0 right-0 left-1/2 h-full", style: {
|
|
@@ -109,7 +125,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
|
|
|
109
125
|
justifyContent: isCentered ? "center" : "",
|
|
110
126
|
gap: "10px",
|
|
111
127
|
} },
|
|
112
|
-
React.createElement("span", { className: "text-[13.33px]", style: strikethroughStyle },
|
|
128
|
+
hasDiscount && !isPeru && (React.createElement("span", { className: "text-[13.33px]", style: strikethroughStyle }, formatPrice(highestOriginalPrice))),
|
|
113
129
|
renderSeatPrices())));
|
|
114
130
|
}
|
|
115
131
|
export default SeatSection;
|
|
@@ -18,6 +18,7 @@ interface DateTimeSectionMobileProps {
|
|
|
18
18
|
currencySign: string;
|
|
19
19
|
availableSeats: number;
|
|
20
20
|
removeDuplicateSeats?: boolean;
|
|
21
|
+
serviceItem?: any;
|
|
21
22
|
}
|
|
22
|
-
declare function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, }: DateTimeSectionMobileProps): React.ReactElement;
|
|
23
|
+
declare function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, }: DateTimeSectionMobileProps): React.ReactElement;
|
|
23
24
|
export default DateTimeSectionMobile;
|
|
@@ -31,7 +31,7 @@ const TimeRow = ({ label, icon, alt, date, timeContent, isSoldOut, }) => (React.
|
|
|
31
31
|
React.createElement("span", { className: "cursor-pointer black-text" }, DateService.getServiceItemDate(date)),
|
|
32
32
|
React.createElement("div", { className: "absolute left-[50%]" }, "\u2022"),
|
|
33
33
|
React.createElement("div", { className: "font-[900] relative black-text" }, timeContent)))));
|
|
34
|
-
function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, }) {
|
|
34
|
+
function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, }) {
|
|
35
35
|
const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
|
|
36
36
|
const depTimeContent = isLinatal ? (React.createElement("div", null,
|
|
37
37
|
React.createElement("span", null,
|
|
@@ -52,6 +52,6 @@ function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal
|
|
|
52
52
|
backgroundColor: "#ccc",
|
|
53
53
|
margin: "auto",
|
|
54
54
|
} })),
|
|
55
|
-
React.createElement(SeatSectionMobile, { seatTypes: seatTypes, isSoldOut: isSoldOut, isPeru: isPeru, seatPriceColor: seatPriceColor, currencySign: currencySign, availableSeats: availableSeats, removeDuplicateSeats: removeDuplicateSeats })));
|
|
55
|
+
React.createElement(SeatSectionMobile, { seatTypes: seatTypes, isSoldOut: isSoldOut, isPeru: isPeru, seatPriceColor: seatPriceColor, currencySign: currencySign, availableSeats: availableSeats, removeDuplicateSeats: removeDuplicateSeats, serviceItem: serviceItem })));
|
|
56
56
|
}
|
|
57
57
|
export default DateTimeSectionMobile;
|
|
@@ -12,6 +12,7 @@ interface SeatSectionMobileProps {
|
|
|
12
12
|
currencySign: string;
|
|
13
13
|
availableSeats: number;
|
|
14
14
|
removeDuplicateSeats?: boolean;
|
|
15
|
+
serviceItem?: any;
|
|
15
16
|
}
|
|
16
|
-
declare function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, }: SeatSectionMobileProps): React.ReactElement;
|
|
17
|
+
declare function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, }: SeatSectionMobileProps): React.ReactElement;
|
|
17
18
|
export default SeatSectionMobile;
|
|
@@ -42,14 +42,15 @@ const getUniqueSeats = (data, limit) => {
|
|
|
42
42
|
.sort((a, b) => a.fare - b.fare)
|
|
43
43
|
.slice(0, limit);
|
|
44
44
|
};
|
|
45
|
-
function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, }) {
|
|
46
|
-
var _a;
|
|
45
|
+
function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, }) {
|
|
46
|
+
var _a, _b, _c;
|
|
47
47
|
const hasMultipleTypes = ((_a = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.length) !== null && _a !== void 0 ? _a : 0) > 2;
|
|
48
48
|
const getFare = (fare) => {
|
|
49
49
|
if (removeDuplicateSeats && availableSeats <= 0 && !isPeru) {
|
|
50
50
|
return commonService.currency(0, currencySign);
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
const { discountedPrice } = commonService.calculateDiscountedPrice(fare, serviceItem);
|
|
53
|
+
return commonService.currency(discountedPrice, currencySign);
|
|
53
54
|
};
|
|
54
55
|
const getLowestFare = () => {
|
|
55
56
|
var _a;
|
|
@@ -67,13 +68,14 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
67
68
|
if (lowestFare === null)
|
|
68
69
|
return null;
|
|
69
70
|
const priceColor = isSoldOut ? "#bbb" : seatPriceColor;
|
|
71
|
+
const { originalPrice, discountedPrice } = commonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
70
72
|
return (React.createElement(React.Fragment, null,
|
|
71
|
-
React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
|
|
73
|
+
originalPrice !== discountedPrice && (React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
|
|
72
74
|
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px]", style: { color: "#bbb" } }, "Antes"),
|
|
73
|
-
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] line-through", style: { color: "#bbb" } }, commonService.currency(
|
|
75
|
+
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] line-through", style: { color: "#bbb" } }, commonService.currency(originalPrice, currencySign)))),
|
|
74
76
|
React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
|
|
75
77
|
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: isSoldOut ? "#bbb" : "#464647" } }, "Desde"),
|
|
76
|
-
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(
|
|
78
|
+
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(discountedPrice, currencySign)))));
|
|
77
79
|
};
|
|
78
80
|
const renderSeats = () => {
|
|
79
81
|
var _a, _b, _c;
|
|
@@ -84,11 +86,17 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
84
86
|
const uniqueSeats = getUniqueSeats(seatTypesData, 3);
|
|
85
87
|
return uniqueSeats.map((type, i) => (React.createElement(SeatRow, { key: i, type: type, index: i, displayLabel: commonService.truncateSeatLabel(type.label), fare: getFare(type.fare), isSoldOut: isSoldOut, seatPriceColor: seatPriceColor, hasMultipleTypes: hasMultipleTypes, textSize: "text-[11px]" })));
|
|
86
88
|
}
|
|
87
|
-
return (_c = (_b = (_a = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label))) === null || _a === void 0 ? void 0 : _a.sort((a, b) => a.fare - b.fare)) === null || _b === void 0 ? void 0 : _b.slice(0, 2)) === null || _c === void 0 ? void 0 : _c.map((type, i) => (React.createElement(SeatRow, { key: i, type: type, index: i, displayLabel: type.label, fare:
|
|
89
|
+
return (_c = (_b = (_a = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label))) === null || _a === void 0 ? void 0 : _a.sort((a, b) => a.fare - b.fare)) === null || _b === void 0 ? void 0 : _b.slice(0, 2)) === null || _c === void 0 ? void 0 : _c.map((type, i) => (React.createElement(SeatRow, { key: i, type: type, index: i, displayLabel: type.label, fare: getFare(type.fare), isSoldOut: isSoldOut, seatPriceColor: seatPriceColor, hasMultipleTypes: hasMultipleTypes, textSize: "text-[12px]" })));
|
|
88
90
|
};
|
|
91
|
+
const seats = removeDuplicateSeats
|
|
92
|
+
? getUniqueSeats(seatTypesData, 3)
|
|
93
|
+
: (_c = (_b = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label))) === null || _b === void 0 ? void 0 : _b.sort((a, b) => a.fare - b.fare)) === null || _c === void 0 ? void 0 : _c.slice(0, 2);
|
|
94
|
+
const discountedSeats = seats === null || seats === void 0 ? void 0 : seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
|
|
95
|
+
const highestOriginalPrice = Math.max(...((discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.map((s) => s.originalPrice)) || [0]));
|
|
96
|
+
const hasDiscount = discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
|
|
89
97
|
return (React.createElement("div", { className: "content-center relative", style: { width: "40%" } },
|
|
90
|
-
!isPeru && (React.createElement("div", { className: "absolute -top-[16px] right-[0] flex justify-end" },
|
|
91
|
-
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] line-through", style: { color: "#bbb" } },
|
|
98
|
+
!isPeru && hasDiscount && (React.createElement("div", { className: "absolute -top-[16px] right-[0] flex justify-end" },
|
|
99
|
+
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] line-through", style: { color: "#bbb" } }, commonService.currency(highestOriginalPrice, currencySign)))),
|
|
92
100
|
React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem] ", style: {
|
|
93
101
|
gap: isSoldOut ? "0px" : "5px",
|
|
94
102
|
justifyContent: hasMultipleTypes ? "space-between" : "center",
|
|
@@ -9,5 +9,13 @@ declare const commonService: {
|
|
|
9
9
|
capitalize: (str: any) => any;
|
|
10
10
|
formatTimeLabel: (label: string) => string;
|
|
11
11
|
formatDefaultLabel: (label: string) => string;
|
|
12
|
+
calculateDiscountedPrice: (originalPrice: number | string, serviceItem?: {
|
|
13
|
+
discount_type?: string;
|
|
14
|
+
discount_value?: number;
|
|
15
|
+
max_discount?: number;
|
|
16
|
+
}) => {
|
|
17
|
+
originalPrice: number;
|
|
18
|
+
discountedPrice: number;
|
|
19
|
+
};
|
|
12
20
|
};
|
|
13
21
|
export default commonService;
|
|
@@ -267,5 +267,22 @@ const commonService = {
|
|
|
267
267
|
return "";
|
|
268
268
|
return label.toLowerCase();
|
|
269
269
|
},
|
|
270
|
+
calculateDiscountedPrice: (originalPrice, serviceItem) => {
|
|
271
|
+
const price = typeof originalPrice === "string"
|
|
272
|
+
? parseFloat(originalPrice)
|
|
273
|
+
: originalPrice;
|
|
274
|
+
if (!serviceItem || !price) {
|
|
275
|
+
return { originalPrice: price, discountedPrice: price };
|
|
276
|
+
}
|
|
277
|
+
const { discount_type, discount_value, max_discount } = serviceItem;
|
|
278
|
+
const percentageDiscount = discount_type === "percentage" && discount_value != null
|
|
279
|
+
? (price * discount_value) / 100
|
|
280
|
+
: 0;
|
|
281
|
+
const finalDiscount = max_discount != null && max_discount > 0
|
|
282
|
+
? Math.min(percentageDiscount, max_discount)
|
|
283
|
+
: percentageDiscount;
|
|
284
|
+
const discountedPrice = Math.max(0, price - finalDiscount);
|
|
285
|
+
return { originalPrice: price, discountedPrice };
|
|
286
|
+
},
|
|
270
287
|
};
|
|
271
288
|
export default commonService;
|
package/package.json
CHANGED
|
@@ -310,8 +310,7 @@ function ServiceItemPB({
|
|
|
310
310
|
const id = setInterval(() => {
|
|
311
311
|
remaining -= 1;
|
|
312
312
|
if (remaining <= 0) {
|
|
313
|
-
remaining =
|
|
314
|
-
clearInterval(id);
|
|
313
|
+
remaining = countdownSeconds;
|
|
315
314
|
}
|
|
316
315
|
node.textContent = formatTime(remaining);
|
|
317
316
|
}, 1000);
|
|
@@ -558,6 +557,7 @@ function ServiceItemPB({
|
|
|
558
557
|
<div className="content-center">
|
|
559
558
|
<SeatSection
|
|
560
559
|
seatTypes={serviceItem.seat_types}
|
|
560
|
+
serviceItem={serviceItem}
|
|
561
561
|
availableSeats={serviceItem.available_seats}
|
|
562
562
|
isSoldOut={isSoldOut}
|
|
563
563
|
priceColor={colors.priceColor}
|
|
@@ -48,6 +48,8 @@ function ServiceItemMobile({
|
|
|
48
48
|
const isItemExpanded = serviceItem.id === isExpanded;
|
|
49
49
|
const isPetSeat = (Object.keys(serviceItem?.pet_seat_info) || []).length > 0;
|
|
50
50
|
let isSoldOut = serviceItem.available_seats <= 0;
|
|
51
|
+
const isLongOfferText = (serviceItem?.offer_text?.length || 0) > 35;
|
|
52
|
+
|
|
51
53
|
const startViewerCount = (node: HTMLSpanElement | null) => {
|
|
52
54
|
if (!node || !viewersConfig) return;
|
|
53
55
|
|
|
@@ -92,8 +94,7 @@ function ServiceItemMobile({
|
|
|
92
94
|
const id = setInterval(() => {
|
|
93
95
|
remaining -= 1;
|
|
94
96
|
if (remaining <= 0) {
|
|
95
|
-
remaining =
|
|
96
|
-
clearInterval(id);
|
|
97
|
+
remaining = countdownSeconds;
|
|
97
98
|
}
|
|
98
99
|
node.textContent = formatTime(remaining);
|
|
99
100
|
}, 1000);
|
|
@@ -176,7 +177,7 @@ function ServiceItemMobile({
|
|
|
176
177
|
// className={`relative ${
|
|
177
178
|
// serviceItem.offer_text ? "mb-[55px]" : "mb-[14px]"
|
|
178
179
|
// }
|
|
179
|
-
className={`relative ${serviceItem.offer_text ? "mb-[
|
|
180
|
+
className={`relative ${!serviceItem.offer_text ? "mb-[14px]" : showTopLabel || serviceItem?.is_direct_trip ? "mb-[20px]" : "mb-[12px]"} ${
|
|
180
181
|
serviceItem?.is_direct_trip ||
|
|
181
182
|
isConexion ||
|
|
182
183
|
serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
|
|
@@ -291,6 +292,7 @@ function ServiceItemMobile({
|
|
|
291
292
|
currencySign={currencySign}
|
|
292
293
|
availableSeats={serviceItem.available_seats}
|
|
293
294
|
removeDuplicateSeats={removeDuplicateSeats}
|
|
295
|
+
serviceItem={serviceItem}
|
|
294
296
|
/>
|
|
295
297
|
{/* <div className="bg-[#E6E6E6] -ml-[12px] -mr-[12px] mt-[10px] mb-[10px] h-[1px]"></div> */}
|
|
296
298
|
<div className="bg-[#E6E6E6] mt-[10px] mb-[10px] h-[1px]"></div>
|
|
@@ -404,7 +406,7 @@ function ServiceItemMobile({
|
|
|
404
406
|
{/* 🔹 EXPANDABLE DROPDOWN (below the card) */}
|
|
405
407
|
{serviceItem?.offer_text && (
|
|
406
408
|
<div
|
|
407
|
-
className="px-[12px] pt-[22px] pb-[
|
|
409
|
+
className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
|
|
408
410
|
style={{
|
|
409
411
|
backgroundColor: isSoldOut ? "#ccc" : colors?.bottomStripColor,
|
|
410
412
|
opacity: isSoldOut ? 0.5 : 1,
|
|
@@ -416,43 +418,49 @@ function ServiceItemMobile({
|
|
|
416
418
|
className="flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]"
|
|
417
419
|
style={{ lineHeight: 1.6 }}
|
|
418
420
|
>
|
|
419
|
-
<div className="flex flex-col">
|
|
420
|
-
<div
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
421
|
+
<div className="flex flex-col gap-[4px]">
|
|
422
|
+
<div
|
|
423
|
+
className={`flex ${isLongOfferText ? "items-start" : "items-center"}`}
|
|
424
|
+
>
|
|
425
|
+
<div className={isLongOfferText ? "mt-[2px]" : ""}>
|
|
426
|
+
<LottiePlayer
|
|
427
|
+
animationData={serviceItem.icons.bombAnim}
|
|
428
|
+
width="18px"
|
|
429
|
+
height="18px"
|
|
430
|
+
/>
|
|
431
|
+
</div>
|
|
426
432
|
<div
|
|
427
|
-
className=
|
|
433
|
+
className={`ml-[6px] flex-1 outline-none ${isLongOfferText ? "mt-[2px]" : ""}`}
|
|
428
434
|
style={{
|
|
429
435
|
color: "#fff",
|
|
436
|
+
lineHeight: 1.4,
|
|
430
437
|
}}
|
|
431
438
|
>
|
|
432
|
-
<span className="
|
|
433
|
-
{serviceItem?.offer_text || ""}
|
|
439
|
+
<span className="min-[380px]:text-[12px] bold-text">
|
|
440
|
+
{serviceItem?.offer_text || ""}
|
|
434
441
|
</span>{" "}
|
|
435
|
-
|
|
436
|
-
<span
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
442
|
+
<span className="min-[380px]:text-[12px]">|</span>{" "}
|
|
443
|
+
<span className="whitespace-nowrap min-[380px]:text-[12px]">
|
|
444
|
+
Termina en
|
|
445
|
+
<span
|
|
446
|
+
className="bold-text"
|
|
447
|
+
ref={startCountdown}
|
|
448
|
+
style={{
|
|
449
|
+
fontVariantNumeric: "tabular-nums",
|
|
450
|
+
display: "inline-block",
|
|
451
|
+
}}
|
|
452
|
+
/>
|
|
453
|
+
</span>
|
|
445
454
|
</div>
|
|
446
455
|
</div>
|
|
447
456
|
<div
|
|
448
|
-
className="flex items-
|
|
457
|
+
className="flex items-start"
|
|
449
458
|
style={{
|
|
450
459
|
color: "#fff",
|
|
451
460
|
}}
|
|
452
461
|
>
|
|
453
|
-
{renderIcon("personIcon", "16px")}
|
|
454
|
-
|
|
455
|
-
<span className="">
|
|
462
|
+
<div>{renderIcon("personIcon", "16px")}</div>
|
|
463
|
+
<span className="flex-1" style={{ lineHeight: 1.4 }}>
|
|
456
464
|
<span
|
|
457
465
|
className="bold-text"
|
|
458
466
|
ref={startViewerCount}
|
|
@@ -17,6 +17,7 @@ interface SeatSectionProps {
|
|
|
17
17
|
currencySign?: string;
|
|
18
18
|
removeDuplicateSeats?: boolean;
|
|
19
19
|
isPeru?: boolean;
|
|
20
|
+
serviceItem?: any;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
@@ -94,6 +95,7 @@ function SeatSection({
|
|
|
94
95
|
currencySign,
|
|
95
96
|
removeDuplicateSeats,
|
|
96
97
|
isPeru,
|
|
98
|
+
serviceItem,
|
|
97
99
|
}: SeatSectionProps): React.ReactElement {
|
|
98
100
|
const uniqueSeats = getUniqueSeats(seatTypes);
|
|
99
101
|
const sortedSeatTypes = getSortedSeatTypes(seatTypes);
|
|
@@ -130,49 +132,50 @@ function SeatSection({
|
|
|
130
132
|
if (isPeru) {
|
|
131
133
|
const allSeats = getAllSeatTypes(seatTypes);
|
|
132
134
|
const lowestFare = allSeats.length > 0 ? allSeats[0].price : 0;
|
|
135
|
+
const { originalPrice, discountedPrice } =
|
|
136
|
+
CommonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
137
|
+
|
|
133
138
|
return (
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
|
|
139
|
+
<>
|
|
140
|
+
{originalPrice !== discountedPrice && (
|
|
141
|
+
<span className="text-[13.33px]" style={strikethroughStyle}>
|
|
142
|
+
{formatPrice(originalPrice)}
|
|
143
|
+
</span>
|
|
144
|
+
)}
|
|
145
|
+
<span className="flex items-center text-[13.33px] bold-text">
|
|
146
|
+
{formatPrice(discountedPrice)}
|
|
147
|
+
</span>
|
|
148
|
+
</>
|
|
137
149
|
);
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
if (removeDuplicateSeats) {
|
|
141
|
-
return uniqueSeats.map((val, key) =>
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
return uniqueSeats.map((val, key) => {
|
|
154
|
+
const { discountedPrice } = CommonService.calculateDiscountedPrice(
|
|
155
|
+
val.price,
|
|
156
|
+
serviceItem,
|
|
157
|
+
);
|
|
158
|
+
return <span key={key}>{formatPrice(discountedPrice)}</span>;
|
|
159
|
+
});
|
|
144
160
|
}
|
|
145
161
|
|
|
146
|
-
return sortedSeatTypes.map((val, key: number) =>
|
|
147
|
-
SEAT_EXCEPTIONS.includes(val.label)
|
|
162
|
+
return sortedSeatTypes.map((val, key: number) => {
|
|
163
|
+
if (SEAT_EXCEPTIONS.includes(val.label)) return null;
|
|
164
|
+
|
|
165
|
+
const { discountedPrice } = CommonService.calculateDiscountedPrice(
|
|
166
|
+
val.price,
|
|
167
|
+
serviceItem,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return (
|
|
148
171
|
<span key={key} className="flex items-center text-[13.33px] bold-text">
|
|
149
|
-
{typeof
|
|
150
|
-
|
|
172
|
+
{typeof discountedPrice === "string" ||
|
|
173
|
+
typeof discountedPrice === "number"
|
|
174
|
+
? formatPrice(discountedPrice)
|
|
151
175
|
: null}
|
|
152
176
|
</span>
|
|
153
|
-
),
|
|
154
|
-
);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const renderLabels = () => {
|
|
158
|
-
if (isPeru) {
|
|
159
|
-
return (
|
|
160
|
-
<>
|
|
161
|
-
<span
|
|
162
|
-
className="text-[13.33px]"
|
|
163
|
-
style={{
|
|
164
|
-
color: "#999",
|
|
165
|
-
// position: "relative",
|
|
166
|
-
// bottom: numberOfSeats ? "10px" : "",
|
|
167
|
-
}}
|
|
168
|
-
>
|
|
169
|
-
Antes
|
|
170
|
-
</span>
|
|
171
|
-
<span className="text-[13.33px]">Desde</span>
|
|
172
|
-
</>
|
|
173
177
|
);
|
|
174
|
-
}
|
|
175
|
-
return renderSeatNames();
|
|
178
|
+
});
|
|
176
179
|
};
|
|
177
180
|
|
|
178
181
|
const strikethroughStyle: React.CSSProperties = {
|
|
@@ -185,6 +188,43 @@ function SeatSection({
|
|
|
185
188
|
: { position: "absolute", top: isCentered ? "-10px" : "-18px" }),
|
|
186
189
|
};
|
|
187
190
|
|
|
191
|
+
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
192
|
+
const discountedSeats = seats.map((seat) => ({
|
|
193
|
+
...seat,
|
|
194
|
+
...CommonService.calculateDiscountedPrice(seat.price, serviceItem),
|
|
195
|
+
}));
|
|
196
|
+
|
|
197
|
+
const highestOriginalPrice = Math.max(
|
|
198
|
+
...discountedSeats.map((seat) => seat.originalPrice),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const hasDiscount = discountedSeats.some(
|
|
202
|
+
(seat) => seat.originalPrice !== seat.discountedPrice,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const renderLabels = () => {
|
|
206
|
+
if (isPeru) {
|
|
207
|
+
return (
|
|
208
|
+
<>
|
|
209
|
+
{hasDiscount && (
|
|
210
|
+
<span
|
|
211
|
+
className="text-[13.33px]"
|
|
212
|
+
style={{
|
|
213
|
+
color: "#999",
|
|
214
|
+
// position: "relative",
|
|
215
|
+
// bottom: numberOfSeats ? "10px" : "",
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
Antes
|
|
219
|
+
</span>
|
|
220
|
+
)}
|
|
221
|
+
<span className="text-[13.33px]">Desde</span>
|
|
222
|
+
</>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return renderSeatNames();
|
|
226
|
+
};
|
|
227
|
+
|
|
188
228
|
return (
|
|
189
229
|
<div
|
|
190
230
|
className="relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]"
|
|
@@ -205,9 +245,17 @@ function SeatSection({
|
|
|
205
245
|
gap: "10px",
|
|
206
246
|
}}
|
|
207
247
|
>
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
248
|
+
{/* {isPeru && (
|
|
249
|
+
<span className="text-[13.33px]" style={strikethroughStyle}>
|
|
250
|
+
{formatPrice(1000)}
|
|
251
|
+
</span>
|
|
252
|
+
)} */}
|
|
253
|
+
|
|
254
|
+
{hasDiscount && !isPeru && (
|
|
255
|
+
<span className="text-[13.33px]" style={strikethroughStyle}>
|
|
256
|
+
{formatPrice(highestOriginalPrice)}
|
|
257
|
+
</span>
|
|
258
|
+
)}
|
|
211
259
|
{renderSeatPrices()}
|
|
212
260
|
</div>
|
|
213
261
|
</div>
|
|
@@ -21,6 +21,7 @@ interface DateTimeSectionMobileProps {
|
|
|
21
21
|
currencySign: string;
|
|
22
22
|
availableSeats: number;
|
|
23
23
|
removeDuplicateSeats?: boolean;
|
|
24
|
+
serviceItem?: any;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
const pad = (n: number) => (n < 10 ? "0" + n : String(n));
|
|
@@ -117,6 +118,7 @@ function DateTimeSectionMobile({
|
|
|
117
118
|
currencySign,
|
|
118
119
|
availableSeats,
|
|
119
120
|
removeDuplicateSeats,
|
|
121
|
+
serviceItem,
|
|
120
122
|
}: DateTimeSectionMobileProps): React.ReactElement {
|
|
121
123
|
const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
|
|
122
124
|
|
|
@@ -184,6 +186,7 @@ function DateTimeSectionMobile({
|
|
|
184
186
|
currencySign={currencySign}
|
|
185
187
|
availableSeats={availableSeats}
|
|
186
188
|
removeDuplicateSeats={removeDuplicateSeats}
|
|
189
|
+
serviceItem={serviceItem}
|
|
187
190
|
/>
|
|
188
191
|
</div>
|
|
189
192
|
);
|
|
@@ -27,6 +27,7 @@ interface SeatSectionMobileProps {
|
|
|
27
27
|
currencySign: string;
|
|
28
28
|
availableSeats: number;
|
|
29
29
|
removeDuplicateSeats?: boolean;
|
|
30
|
+
serviceItem?: any;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
interface SeatRowProps {
|
|
@@ -110,6 +111,7 @@ function SeatSectionMobile({
|
|
|
110
111
|
currencySign,
|
|
111
112
|
availableSeats,
|
|
112
113
|
removeDuplicateSeats,
|
|
114
|
+
serviceItem,
|
|
113
115
|
}: SeatSectionMobileProps): React.ReactElement {
|
|
114
116
|
const hasMultipleTypes = (seatTypesData?.length ?? 0) > 2;
|
|
115
117
|
|
|
@@ -117,7 +119,11 @@ function SeatSectionMobile({
|
|
|
117
119
|
if (removeDuplicateSeats && availableSeats <= 0 && !isPeru) {
|
|
118
120
|
return commonService.currency(0, currencySign);
|
|
119
121
|
}
|
|
120
|
-
|
|
122
|
+
const { discountedPrice } = commonService.calculateDiscountedPrice(
|
|
123
|
+
fare,
|
|
124
|
+
serviceItem,
|
|
125
|
+
);
|
|
126
|
+
return commonService.currency(discountedPrice, currencySign);
|
|
121
127
|
};
|
|
122
128
|
|
|
123
129
|
const getLowestFare = (): number | null => {
|
|
@@ -144,23 +150,27 @@ function SeatSectionMobile({
|
|
|
144
150
|
if (lowestFare === null) return null;
|
|
145
151
|
|
|
146
152
|
const priceColor = isSoldOut ? "#bbb" : seatPriceColor;
|
|
153
|
+
const { originalPrice, discountedPrice } =
|
|
154
|
+
commonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
147
155
|
|
|
148
156
|
return (
|
|
149
157
|
<>
|
|
150
|
-
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
{originalPrice !== discountedPrice && (
|
|
159
|
+
<div className="w-[100%] flex flex-row justify-between items-center">
|
|
160
|
+
<span
|
|
161
|
+
className="min-[420]:text-[13px] text-[12px]"
|
|
162
|
+
style={{ color: "#bbb" }}
|
|
163
|
+
>
|
|
164
|
+
Antes
|
|
165
|
+
</span>
|
|
166
|
+
<span
|
|
167
|
+
className="min-[420]:text-[13px] text-[12px] line-through"
|
|
168
|
+
style={{ color: "#bbb" }}
|
|
169
|
+
>
|
|
170
|
+
{commonService.currency(originalPrice, currencySign)}
|
|
171
|
+
</span>
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
164
174
|
<div className="w-[100%] flex flex-row justify-between items-center">
|
|
165
175
|
<span
|
|
166
176
|
className="min-[420]:text-[13px] text-[12px] bold-text"
|
|
@@ -172,7 +182,7 @@ function SeatSectionMobile({
|
|
|
172
182
|
className="min-[420]:text-[13px] text-[12px] bold-text"
|
|
173
183
|
style={{ color: priceColor }}
|
|
174
184
|
>
|
|
175
|
-
{commonService.currency(
|
|
185
|
+
{commonService.currency(discountedPrice, currencySign)}
|
|
176
186
|
</span>
|
|
177
187
|
</div>
|
|
178
188
|
</>
|
|
@@ -211,7 +221,7 @@ function SeatSectionMobile({
|
|
|
211
221
|
type={type}
|
|
212
222
|
index={i}
|
|
213
223
|
displayLabel={type.label}
|
|
214
|
-
fare={
|
|
224
|
+
fare={getFare(type.fare)}
|
|
215
225
|
isSoldOut={isSoldOut}
|
|
216
226
|
seatPriceColor={seatPriceColor}
|
|
217
227
|
hasMultipleTypes={hasMultipleTypes}
|
|
@@ -220,15 +230,34 @@ function SeatSectionMobile({
|
|
|
220
230
|
));
|
|
221
231
|
};
|
|
222
232
|
|
|
233
|
+
const seats = removeDuplicateSeats
|
|
234
|
+
? getUniqueSeats(seatTypesData, 3)
|
|
235
|
+
: seatTypesData
|
|
236
|
+
?.filter((item) => getFilteredSeats(item.label))
|
|
237
|
+
?.sort((a, b) => a.fare - b.fare)
|
|
238
|
+
?.slice(0, 2);
|
|
239
|
+
|
|
240
|
+
const discountedSeats = seats?.map((seat) => ({
|
|
241
|
+
...seat,
|
|
242
|
+
...commonService.calculateDiscountedPrice(seat.fare, serviceItem),
|
|
243
|
+
}));
|
|
244
|
+
|
|
245
|
+
const highestOriginalPrice = Math.max(
|
|
246
|
+
...(discountedSeats?.map((s) => s.originalPrice) || [0]),
|
|
247
|
+
);
|
|
248
|
+
const hasDiscount = discountedSeats?.some(
|
|
249
|
+
(s) => s.originalPrice !== s.discountedPrice,
|
|
250
|
+
);
|
|
251
|
+
|
|
223
252
|
return (
|
|
224
253
|
<div className="content-center relative" style={{ width: "40%" }}>
|
|
225
|
-
{!isPeru && (
|
|
254
|
+
{!isPeru && hasDiscount && (
|
|
226
255
|
<div className="absolute -top-[16px] right-[0] flex justify-end">
|
|
227
256
|
<span
|
|
228
257
|
className="min-[420]:text-[13px] text-[12px] line-through"
|
|
229
258
|
style={{ color: "#bbb" }}
|
|
230
259
|
>
|
|
231
|
-
|
|
260
|
+
{commonService.currency(highestOriginalPrice, currencySign)}
|
|
232
261
|
</span>
|
|
233
262
|
</div>
|
|
234
263
|
)}
|
|
@@ -274,6 +274,40 @@ const commonService = {
|
|
|
274
274
|
if (!label) return "";
|
|
275
275
|
return label.toLowerCase();
|
|
276
276
|
},
|
|
277
|
+
|
|
278
|
+
calculateDiscountedPrice: (
|
|
279
|
+
originalPrice: number | string,
|
|
280
|
+
serviceItem?: {
|
|
281
|
+
discount_type?: string;
|
|
282
|
+
discount_value?: number;
|
|
283
|
+
max_discount?: number;
|
|
284
|
+
},
|
|
285
|
+
): { originalPrice: number; discountedPrice: number } => {
|
|
286
|
+
const price =
|
|
287
|
+
typeof originalPrice === "string"
|
|
288
|
+
? parseFloat(originalPrice)
|
|
289
|
+
: originalPrice;
|
|
290
|
+
|
|
291
|
+
if (!serviceItem || !price) {
|
|
292
|
+
return { originalPrice: price, discountedPrice: price };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const { discount_type, discount_value, max_discount } = serviceItem;
|
|
296
|
+
|
|
297
|
+
const percentageDiscount =
|
|
298
|
+
discount_type === "percentage" && discount_value != null
|
|
299
|
+
? (price * discount_value) / 100
|
|
300
|
+
: 0;
|
|
301
|
+
|
|
302
|
+
const finalDiscount =
|
|
303
|
+
max_discount != null && max_discount > 0
|
|
304
|
+
? Math.min(percentageDiscount, max_discount)
|
|
305
|
+
: percentageDiscount;
|
|
306
|
+
|
|
307
|
+
const discountedPrice = Math.max(0, price - finalDiscount);
|
|
308
|
+
|
|
309
|
+
return { originalPrice: price, discountedPrice };
|
|
310
|
+
},
|
|
277
311
|
};
|
|
278
312
|
|
|
279
313
|
export default commonService;
|