kupos-ui-components-lib 9.1.8 → 9.1.9

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.
@@ -303,7 +303,7 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
303
303
  backgroundColor: "#ccc",
304
304
  } }),
305
305
  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 })),
306
+ 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
307
  React.createElement("div", null,
308
308
  showLastSeats ? (React.createElement("div", { className: "flex justify-end mr-[11px] ", style: {
309
309
  position: "absolute",
@@ -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,7 +141,7 @@ 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: (_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, serviceItem: serviceItem }),
145
145
  React.createElement("div", { className: "bg-[#E6E6E6] mt-[10px] mb-[10px] h-[1px]" }),
146
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),
147
147
  // onDropdownToggle={() => {
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
  }
@@ -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,14 +74,26 @@ 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
  };
86
98
  const renderLabels = () => {
87
99
  if (isPeru) {
@@ -98,6 +110,10 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
98
110
  const strikethroughStyle = Object.assign({ color: "#ccc", display: "flex", textAlign: "end", textDecoration: "line-through" }, (isPeru
99
111
  ? { position: "relative", top: 0 }
100
112
  : { position: "absolute", top: isCentered ? "-10px" : "-18px" }));
113
+ const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
114
+ const discountedSeats = seats.map((seat) => (Object.assign(Object.assign({}, seat), CommonService.calculateDiscountedPrice(seat.price, serviceItem))));
115
+ const highestOriginalPrice = Math.max(...discountedSeats.map((seat) => seat.originalPrice));
116
+ const hasDiscount = discountedSeats.some((seat) => seat.originalPrice !== seat.discountedPrice);
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,8 @@ 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
+ isPeru && (React.createElement("span", { className: "text-[13.33px]", style: strikethroughStyle }, formatPrice(1000))),
129
+ hasDiscount && (React.createElement("span", { className: "text-[13.33px]", style: strikethroughStyle }, formatPrice(highestOriginalPrice))),
113
130
  renderSeatPrices())));
114
131
  }
115
132
  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,16 @@ 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
73
  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" } }, originalPrice !== discountedPrice
76
+ ? commonService.currency(originalPrice, currencySign)
77
+ : commonService.currency(getHighestFare(), currencySign))),
74
78
  React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
75
79
  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)))));
80
+ React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(discountedPrice, currencySign)))));
77
81
  };
78
82
  const renderSeats = () => {
79
83
  var _a, _b, _c;
@@ -84,11 +88,17 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
84
88
  const uniqueSeats = getUniqueSeats(seatTypesData, 3);
85
89
  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
90
  }
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]" })));
91
+ 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
92
  };
93
+ const seats = removeDuplicateSeats
94
+ ? getUniqueSeats(seatTypesData, 3)
95
+ : (_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);
96
+ const discountedSeats = seats === null || seats === void 0 ? void 0 : seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
97
+ const highestOriginalPrice = Math.max(...((discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.map((s) => s.originalPrice)) || [0]));
98
+ const hasDiscount = discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
89
99
  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"))),
100
+ !isPeru && hasDiscount && (React.createElement("div", { className: "absolute -top-[16px] right-[0] flex justify-end" },
101
+ React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] line-through", style: { color: "#bbb" } }, commonService.currency(highestOriginalPrice, currencySign)))),
92
102
  React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem] ", style: {
93
103
  gap: isSoldOut ? "0px" : "5px",
94
104
  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.9",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -558,6 +558,7 @@ function ServiceItemPB({
558
558
  <div className="content-center">
559
559
  <SeatSection
560
560
  seatTypes={serviceItem.seat_types}
561
+ serviceItem={serviceItem}
561
562
  availableSeats={serviceItem.available_seats}
562
563
  isSoldOut={isSoldOut}
563
564
  priceColor={colors.priceColor}
@@ -176,7 +176,7 @@ function ServiceItemMobile({
176
176
  // className={`relative ${
177
177
  // serviceItem.offer_text ? "mb-[55px]" : "mb-[14px]"
178
178
  // }
179
- className={`relative ${serviceItem.offer_text ? "mb-[20px]" : "mb-[20px]"} ${
179
+ className={`relative ${!serviceItem.offer_text ? "mb-[14px]" : showTopLabel || serviceItem?.is_direct_trip ? "mb-[20px]" : "mb-[12px]"} ${
180
180
  serviceItem?.is_direct_trip ||
181
181
  isConexion ||
182
182
  serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
@@ -291,6 +291,7 @@ function ServiceItemMobile({
291
291
  currencySign={currencySign}
292
292
  availableSeats={serviceItem.available_seats}
293
293
  removeDuplicateSeats={removeDuplicateSeats}
294
+ serviceItem={serviceItem}
294
295
  />
295
296
  {/* <div className="bg-[#E6E6E6] -ml-[12px] -mr-[12px] mt-[10px] mb-[10px] h-[1px]"></div> */}
296
297
  <div className="bg-[#E6E6E6] mt-[10px] mb-[10px] h-[1px]"></div>
@@ -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,28 +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
- );
177
+ );
178
+ });
155
179
  };
156
180
 
157
181
  const renderLabels = () => {
@@ -185,6 +209,20 @@ function SeatSection({
185
209
  : { position: "absolute", top: isCentered ? "-10px" : "-18px" }),
186
210
  };
187
211
 
212
+ const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
213
+ const discountedSeats = seats.map((seat) => ({
214
+ ...seat,
215
+ ...CommonService.calculateDiscountedPrice(seat.price, serviceItem),
216
+ }));
217
+
218
+ const highestOriginalPrice = Math.max(
219
+ ...discountedSeats.map((seat) => seat.originalPrice),
220
+ );
221
+
222
+ const hasDiscount = discountedSeats.some(
223
+ (seat) => seat.originalPrice !== seat.discountedPrice,
224
+ );
225
+
188
226
  return (
189
227
  <div
190
228
  className="relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]"
@@ -205,9 +243,17 @@ function SeatSection({
205
243
  gap: "10px",
206
244
  }}
207
245
  >
208
- <span className="text-[13.33px]" style={strikethroughStyle}>
209
- $1.000
210
- </span>
246
+ {isPeru && (
247
+ <span className="text-[13.33px]" style={strikethroughStyle}>
248
+ {formatPrice(1000)}
249
+ </span>
250
+ )}
251
+
252
+ {hasDiscount && (
253
+ <span className="text-[13.33px]" style={strikethroughStyle}>
254
+ {formatPrice(highestOriginalPrice)}
255
+ </span>
256
+ )}
211
257
  {renderSeatPrices()}
212
258
  </div>
213
259
  </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,6 +150,8 @@ 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
  <>
@@ -158,7 +166,9 @@ function SeatSectionMobile({
158
166
  className="min-[420]:text-[13px] text-[12px] line-through"
159
167
  style={{ color: "#bbb" }}
160
168
  >
161
- {commonService.currency(getHighestFare(), currencySign)}
169
+ {originalPrice !== discountedPrice
170
+ ? commonService.currency(originalPrice, currencySign)
171
+ : commonService.currency(getHighestFare(), currencySign)}
162
172
  </span>
163
173
  </div>
164
174
  <div className="w-[100%] flex flex-row justify-between items-center">
@@ -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;