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.
@@ -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 = 0;
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 = 0;
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-[20px]" : "mb-[20px]"} ${(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ||
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: (_a = serviceItem.icons) === null || _a === void 0 ? void 0 : _a.origin, destinationIcon: (_b = serviceItem.icons) === null || _b === void 0 ? void 0 : _b.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 }),
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: (_c = serviceItem.duration) === null || _c === void 0 ? void 0 : _c.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),
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-[12px] relative -z-9 -mt-[15px]", style: {
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: "flex items-center" },
189
- React.createElement(LottiePlayer, { animationData: serviceItem.icons.bombAnim, width: "18px", height: "18px" }),
190
- React.createElement("div", { className: "ml-[6px] flex items-center mt-[2px]", style: {
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: " min-[380px]:text-[12px]" },
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
- "| Termina en\u00A0",
198
- React.createElement("span", { className: "bold-text", ref: startCountdown, style: {
199
- fontVariantNumeric: "tabular-nums",
200
- display: "inline-block",
201
- // minWidth: "70px",
202
- } }))),
203
- React.createElement("div", { className: "flex items-center", style: {
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
- "\u00A0",
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
- return (React.createElement("span", { className: "flex items-center text-[13.33px] bold-text" }, formatPrice(lowestFare)));
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) => (React.createElement("span", { key: key }, formatPrice(val.price))));
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) => SEAT_EXCEPTIONS.includes(val.label) ? null : (React.createElement("span", { key: key, className: "flex items-center text-[13.33px] bold-text" }, typeof val.price === "string" || typeof val.price === "number"
83
- ? formatPrice(val.price)
84
- : null)));
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 }, "$1.000"),
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
- return commonService.currency(fare, currencySign);
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(getHighestFare(), currencySign))),
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(lowestFare, currencySign)))));
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: commonService.currency(type.fare, currencySign), isSoldOut: isSoldOut, seatPriceColor: seatPriceColor, hasMultipleTypes: hasMultipleTypes, textSize: "text-[12px]" })));
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" } }, "$7.000"))),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "9.1.8",
3
+ "version": "9.1.10",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -310,8 +310,7 @@ function ServiceItemPB({
310
310
  const id = setInterval(() => {
311
311
  remaining -= 1;
312
312
  if (remaining <= 0) {
313
- remaining = 0;
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 = 0;
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-[20px]" : "mb-[20px]"} ${
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-[12px] relative -z-9 -mt-[15px]"
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 className="flex items-center">
421
- <LottiePlayer
422
- animationData={serviceItem.icons.bombAnim}
423
- width="18px"
424
- height="18px"
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="ml-[6px] flex items-center mt-[2px]"
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=" min-[380px]:text-[12px]">
433
- {serviceItem?.offer_text || ""}&nbsp;
439
+ <span className="min-[380px]:text-[12px] bold-text">
440
+ {serviceItem?.offer_text || ""}
434
441
  </span>{" "}
435
- | Termina en&nbsp;
436
- <span
437
- className="bold-text"
438
- ref={startCountdown}
439
- style={{
440
- fontVariantNumeric: "tabular-nums",
441
- display: "inline-block",
442
- // minWidth: "70px",
443
- }}
444
- />
442
+ <span className="min-[380px]:text-[12px]">|</span>{" "}
443
+ <span className="whitespace-nowrap min-[380px]:text-[12px]">
444
+ Termina en&nbsp;
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-center"
457
+ className="flex items-start"
449
458
  style={{
450
459
  color: "#fff",
451
460
  }}
452
461
  >
453
- {renderIcon("personIcon", "16px")}
454
- &nbsp;
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
- <span className="flex items-center text-[13.33px] bold-text">
135
- {formatPrice(lowestFare)}
136
- </span>
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
- <span key={key}>{formatPrice(val.price)}</span>
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) ? null : (
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 val.price === "string" || typeof val.price === "number"
150
- ? formatPrice(val.price)
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
- <span className="text-[13.33px]" style={strikethroughStyle}>
209
- $1.000
210
- </span>
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
- return commonService.currency(fare, currencySign);
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
- <div className="w-[100%] flex flex-row justify-between items-center">
151
- <span
152
- className="min-[420]:text-[13px] text-[12px]"
153
- style={{ color: "#bbb" }}
154
- >
155
- Antes
156
- </span>
157
- <span
158
- className="min-[420]:text-[13px] text-[12px] line-through"
159
- style={{ color: "#bbb" }}
160
- >
161
- {commonService.currency(getHighestFare(), currencySign)}
162
- </span>
163
- </div>
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(lowestFare, currencySign)}
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={commonService.currency(type.fare, currencySign)}
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
- $7.000
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;