kupos-ui-components-lib 9.3.5 → 9.3.7

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.
@@ -68,7 +68,6 @@ const ANIMATION_MAP = {
68
68
  };
69
69
  function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, children, busStage, serviceDetailsLoading, cityOrigin, cityDestination, translation, orignLabel, destinationLabel, currencySign, isCiva, showRating, showLastSeats, removeArrivalTime, removeDuplicateSeats, isPeruSites, showAvailableSeats, isSeatIcon, isLinatal, isPeru, t = (key) => key, siteType, isAllinBus, isExpand, setIsExpand, coachKey, viewersConfig, }) {
70
70
  var _a;
71
- console.log("🚀 ~ ServiceItemPB ~ serviceItem:", serviceItem);
72
71
  const getAnimationIcon = (icon) => {
73
72
  var _a;
74
73
  const animation = ANIMATION_MAP[icon];
@@ -295,7 +294,9 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
295
294
  React.createElement(LottiePlayer, { animationData: getAnimationIcon("bombAnimation"), width: "18px", height: "18px" }),
296
295
  React.createElement("div", { className: "flex items-center mt-[2px]" },
297
296
  React.createElement("span", { className: "bold-text ml-[6px]" },
298
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || "",
297
+ ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || "").length > 30
298
+ ? ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || "").slice(0, 30) + "..."
299
+ : (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || "",
299
300
  "\u00A0"),
300
301
  " ",
301
302
  "| Termina en\u00A0",
@@ -317,7 +318,7 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
317
318
  " |",
318
319
  " ",
319
320
  React.createElement("span", { className: "" },
320
- "\u26A1 Quedan pocos",
321
+ "Quedan pocos.",
321
322
  " ",
322
323
  React.createElement("span", { className: "bold-text", ref: (node) => CommonService.startComprandoCount(node, 4, 16), style: { fontVariantNumeric: "tabular-nums" } }),
323
324
  " ",
@@ -18,13 +18,18 @@ const exceptions = [
18
18
  "asiento_mascota",
19
19
  ];
20
20
  function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, }) {
21
- var _a, _b, _c, _d;
21
+ var _a, _b, _c, _d, _e;
22
22
  const isItemExpanded = serviceItem.id === isExpanded;
23
23
  const isPetSeat = (Object.keys(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.pet_seat_info) || []).length > 0;
24
24
  let isSoldOut = serviceItem.available_seats <= 0;
25
- const isLongOfferText = (((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) === null || _a === void 0 ? void 0 : _a.length) || 0) > 35;
25
+ const seats = removeDuplicateSeats
26
+ ? ((_a = serviceItem.seat_types) === null || _a === void 0 ? void 0 : _a.filter((seat, index, self) => index === self.findIndex((s) => s.label === seat.label))) || []
27
+ : serviceItem.seat_types || [];
28
+ const discountedSeats = seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
29
+ const hasDiscount = discountedSeats.some((seat) => seat.originalPrice !== seat.discountedPrice);
30
+ const isLongOfferText = (((_b = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) === null || _b === void 0 ? void 0 : _b.length) || 0) > 35;
26
31
  const hasOfferText = Boolean(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text);
27
- const offerGradient = "linear-gradient(90deg, #ff5964 0%, #ff8842 100%)";
32
+ const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
28
33
  const serviceCardStyle = hasOfferText
29
34
  ? {
30
35
  borderColor: "transparent",
@@ -92,7 +97,9 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
92
97
  : "rounded-[10px] border border-[#ccc]"}`, style: serviceCardStyle },
93
98
  React.createElement("div", { style: { padding: "12px 12px 8px 12px" } },
94
99
  React.createElement("div", { className: "flex justify-between items-center mb-[10px]" },
95
- React.createElement("div", { className: "flex items-center justify-between", style: { marginBottom: (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) ? "10px" : "" } },
100
+ React.createElement("div", { className: "flex items-center justify-between", style: {
101
+ marginBottom: (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || hasDiscount ? "10px" : "",
102
+ } },
96
103
  React.createElement("div", { className: "w-[120px] overflow-y-hidden" },
97
104
  React.createElement("img", { src: serviceItem.operator_details[0], alt: "service logo", className: `w-[100px] h-auto object-contain ${isSoldOut ? "grayscale" : ""}` })),
98
105
  isCiva ? (React.createElement("div", { className: "black-text min-[420]:text-[12px] text-[12px]" }, serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_details[2])) : showRating ? (React.createElement("div", { className: "flex min-[420]:text-[13px] text-[12px] items-center" },
@@ -104,9 +111,9 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
104
111
  React.createElement("span", { className: "ml-[3px] min-[420]:text-[13px] text-[12px] text-ellipsis overflow-hidden whitespace-nowrap max-w-[120px]" }, serviceItem.operator_details[2]))))) : null),
105
112
  showLastSeats ? (React.createElement("div", { className: "flex justify-end " }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) < 10 &&
106
113
  (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),
107
- 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, tooltipBgColor: colors.tooltipBgColor, currencySign: currencySign, availableSeats: serviceItem.available_seats, removeDuplicateSeats: removeDuplicateSeats, serviceItem: serviceItem }),
114
+ React.createElement(DateTimeSectionMobile, { onBookButtonPress: onBookButtonPress, isCiva: isCiva, isSoldOut: isSoldOut, isLinatal: isLinatal, isPeru: isPeru, orignLabel: orignLabel, destinationLabel: destinationLabel, originIcon: (_c = serviceItem.icons) === null || _c === void 0 ? void 0 : _c.origin, destinationIcon: (_d = serviceItem.icons) === null || _d === void 0 ? void 0 : _d.destination, travelDate: serviceItem.travel_date, arrivalDate: serviceItem.arrival_date, depTime: serviceItem.dep_time, arrTime: serviceItem.arr_time, seatTypes: serviceItem.seat_types, seatPriceColor: colors.seatPriceColor, tooltipBgColor: colors.tooltipBgColor, currencySign: currencySign, availableSeats: serviceItem.available_seats, removeDuplicateSeats: removeDuplicateSeats, serviceItem: serviceItem }),
108
115
  React.createElement("div", { className: "bg-[#E6E6E6] mt-[10px] mb-[10px] h-[1px]" }),
109
- 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), onDropdownToggle: () => {
116
+ React.createElement(BottomAmenitiesMobile, { isSoldOut: isSoldOut, amenitiesNodes: amenities(), hoursIcon: renderIcon("hours", "14px"), duration: (_e = serviceItem.duration) === null || _e === void 0 ? void 0 : _e.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), onDropdownToggle: () => {
110
117
  setIsExpanded(isItemExpanded ? null : serviceItem.id);
111
118
  }, isPeru: isPeru })),
112
119
  React.createElement(ServiceBadgesMobile, { showTopLabel: showTopLabel, isSoldOut: isSoldOut, colors: colors, renderIcon: renderIcon, serviceItem: serviceItem, isConexion: isConexion })),
@@ -118,7 +125,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
118
125
  } },
119
126
  React.createElement("div", { className: "flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]", style: { lineHeight: 1.6 } },
120
127
  React.createElement("div", { className: "flex justify-between items-start" },
121
- React.createElement("div", { className: `flex ${isLongOfferText ? "items-start" : "items-center"}` },
128
+ React.createElement("div", { className: `flex ${((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || "").length > 10 ? "items-start" : "items-center"}` },
122
129
  React.createElement("div", { className: isLongOfferText ? "mt-[2px]" : "" },
123
130
  React.createElement(LottiePlayer, { animationData: serviceItem.icons.bombAnim, width: "14px", height: "14px" })),
124
131
  React.createElement("div", { className: `ml-[4px] flex-1 outline-none ${isLongOfferText ? "mt-[2px]" : ""}`, style: {
@@ -148,7 +155,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
148
155
  "viendo"))),
149
156
  React.createElement("div", { className: "flex items-center" },
150
157
  React.createElement("span", { className: "whitespace-nowrap" },
151
- "\u26A1 Quedan pocos",
158
+ "Quedan pocos.",
152
159
  " ",
153
160
  React.createElement("span", { className: "bold-text", ref: (node) => commonService.startComprandoCount(node, 4, 16), style: { fontVariantNumeric: "tabular-nums" } }),
154
161
  " ",
@@ -155,6 +155,8 @@ export interface MobileServiceItemProps {
155
155
  lastSeatText?: string;
156
156
  lastSeatBg?: string;
157
157
  seatPriceColor?: string;
158
+ rightGradiantColor?: string;
159
+ leftGradiantColor?: string;
158
160
  };
159
161
  isCiva?: boolean;
160
162
  currencySign?: string;
@@ -145,8 +145,9 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
145
145
  React.createElement("div", { className: "col-start-2 row-start-3 flex h-[30px] items-end justify-start" },
146
146
  React.createElement("span", { className: "flex items-center gap-[6px] text-[22px] bold-text leading-[30px]", style: { color: isSoldOut ? "#c0c0c0" : "#ff5964" } },
147
147
  renderIcon("fireIcon", "16px"),
148
- currencySign || "$",
149
- Math.floor((discountSeat.discountedPrice / 1000) * 1000) / 1000))));
148
+ availableSeats <= 0
149
+ ? CommonService.currency(0, currencySign)
150
+ : CommonService.discountedCurrency(discountSeat.discountedPrice, currencySign)))));
150
151
  }
151
152
  return (React.createElement("div", { className: "relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]", style: isCentered ? { alignItems: "center" } : {} },
152
153
  React.createElement("div", { className: "flex flex-col justify-between", style: { gap: "10px" } }, renderLabels()),
@@ -46,12 +46,12 @@ function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal
46
46
  React.createElement("div", { className: "min-h-[2.5rem] flex flex-col justify-between gap-[4px] w-[50%] ", style: { justifyContent: isCiva && "center" } },
47
47
  React.createElement(TimeRow, { label: orignLabel, icon: originIcon, alt: "origin", date: travelDate, timeContent: depTimeContent, isSoldOut: isSoldOut }),
48
48
  isCiva ? null : (React.createElement(TimeRow, { label: destinationLabel, icon: destinationIcon, alt: "destination", date: arrivalDate, timeContent: DateService.formatTime(arrTime), isSoldOut: isSoldOut }))),
49
- isPeru ? null : (React.createElement("div", { style: {
49
+ React.createElement("div", { style: {
50
50
  width: "1px",
51
51
  height: "2.5rem",
52
52
  backgroundColor: "#ccc",
53
53
  margin: "auto",
54
- } })),
54
+ } }),
55
55
  React.createElement(SeatSectionMobile, { seatTypes: seatTypes, isSoldOut: isSoldOut, isPeru: isPeru, seatPriceColor: seatPriceColor, currencySign: currencySign, availableSeats: availableSeats, removeDuplicateSeats: removeDuplicateSeats, serviceItem: serviceItem, tooltipBgColor: tooltipBgColor })));
56
56
  }
57
57
  export default DateTimeSectionMobile;
@@ -88,8 +88,16 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
88
88
  ? getUniqueSeats(seatTypesData, 3)
89
89
  : (_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);
90
90
  const discountedSeats = seats === null || seats === void 0 ? void 0 : seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
91
- const hasDiscount = discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
92
- const discountSeat = (_d = discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.filter((seat) => !EXCEPTIONS.includes(seat.label))) === null || _d === void 0 ? void 0 : _d.sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
91
+ const peruLowestFare = isPeru ? getLowestFare() : null;
92
+ const peruDiscountCalc = isPeru && peruLowestFare != null
93
+ ? commonService.calculateDiscountedPrice(peruLowestFare, serviceItem)
94
+ : null;
95
+ const hasDiscount = isPeru
96
+ ? peruDiscountCalc != null &&
97
+ peruDiscountCalc.originalPrice !== peruDiscountCalc.discountedPrice
98
+ : discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
99
+ const discountSeat = isPeru && peruDiscountCalc
100
+ ? Object.assign({ label: "", fare: peruLowestFare }, peruDiscountCalc) : (_d = discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.filter((seat) => !EXCEPTIONS.includes(seat.label))) === null || _d === void 0 ? void 0 : _d.sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
93
101
  const discountValue = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_type) === "percentage" &&
94
102
  typeof (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) === "number"
95
103
  ? Math.round(serviceItem.discount_value)
@@ -104,11 +112,26 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
104
112
  discountValue,
105
113
  "% OFF"))),
106
114
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] leading-[20px] text-[#c2c2c2]" }, "Antes"),
107
- React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] leading-[20px] text-[#9f9f9f] line-through text-right" }, commonService.currency(discountSeat.originalPrice, currencySign)),
115
+ React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] leading-[20px] text-[#9f9f9f] text-right", style: {
116
+ position: "relative",
117
+ display: "inline-block",
118
+ overflow: "hidden",
119
+ } },
120
+ commonService.currency(discountSeat.originalPrice, currencySign),
121
+ React.createElement("span", { style: {
122
+ position: "absolute",
123
+ top: "50%",
124
+ left: "25%",
125
+ width: "80%",
126
+ height: "1px",
127
+ background: "#9f9f9f",
128
+ transform: "rotate(-15deg)",
129
+ transformOrigin: "center",
130
+ } })),
108
131
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] leading-[24px]", style: { color: isSoldOut ? "#bbb" : "#464647" } }, "Desde"),
109
132
  React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: { color: isSoldOut ? "#bbb" : "#ff5964" } },
110
133
  ((_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _e === void 0 ? void 0 : _e.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[16px] w-[16px] object-contain", style: { filter: isSoldOut ? "grayscale" : "" } })) : null,
111
- commonService.currency(discountSeat.discountedPrice, currencySign)),
134
+ commonService.discountedCurrency(discountSeat.discountedPrice, currencySign)),
112
135
  isSoldOut ? (React.createElement("span", { className: "col-span-2 min-[420]:text-[13px] text-right text-[12px] text-[#ccc]" }, "Agotado")) : null)) : (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem] ", style: {
113
136
  gap: isSoldOut ? "0px" : "5px",
114
137
  justifyContent: hasMultipleTypes ? "space-between" : "center",
@@ -1,5 +1,6 @@
1
1
  declare const commonService: {
2
2
  currency(amount: number, currencySign?: string): string;
3
+ discountedCurrency(amount: number, currencySign?: string): string;
3
4
  copyObject: (ob: any) => any;
4
5
  getServiceTypeLabelForFilters: (service_type: any) => "Tipo de servicio" | "Punto de embarque" | "Tipo de asiento" | "SERVICIOS" | "";
5
6
  truncateSeatLabel: (label: string | number) => string;
@@ -6,6 +6,13 @@ const commonService = {
6
6
  const sign = currencySign || "$";
7
7
  return sign + formattedAmount;
8
8
  },
9
+ discountedCurrency(amount, currencySign) {
10
+ const formattedAmount = Math.trunc(amount)
11
+ .toString()
12
+ .replace(/\B(?=(\d{3})+(?!\d))/g, ".");
13
+ const sign = currencySign || "$";
14
+ return sign + formattedAmount;
15
+ },
9
16
  copyObject: (ob) => {
10
17
  if (!ob)
11
18
  return {};
@@ -308,21 +315,20 @@ const commonService = {
308
315
  const prevId = node.dataset.countdownId;
309
316
  if (prevId)
310
317
  clearInterval(Number(prevId));
311
- let remaining = countdownSeconds * 1000; // Convert to milliseconds
312
- const formatTime = (totalMs) => {
313
- const m = Math.floor(totalMs / 60000);
314
- const s = Math.floor((totalMs % 60000) / 1000);
315
- const ms = Math.floor((totalMs % 1000) / 10); // Show 2 digits for milliseconds
316
- return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}:${String(ms).padStart(2, "0")}`;
318
+ let remaining = countdownSeconds;
319
+ const formatTime = (totalSeconds) => {
320
+ const m = Math.floor(totalSeconds / 60);
321
+ const s = totalSeconds % 60;
322
+ return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
317
323
  };
318
324
  node.textContent = formatTime(remaining);
319
325
  const id = setInterval(() => {
320
- remaining -= 100; // Decrease by 100ms
326
+ remaining -= 1;
321
327
  if (remaining <= 0) {
322
- remaining = countdownSeconds * 1000;
328
+ remaining = countdownSeconds;
323
329
  }
324
330
  node.textContent = formatTime(remaining);
325
- }, 100); // Update every 100ms
331
+ }, 1000);
326
332
  node.dataset.countdownId = String(id);
327
333
  },
328
334
  startComprandoCount: (node, min = 4, max = 16) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "9.3.5",
3
+ "version": "9.3.7",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -106,7 +106,6 @@ function ServiceItemPB({
106
106
  coachKey,
107
107
  viewersConfig,
108
108
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
109
- console.log("🚀 ~ ServiceItemPB ~ serviceItem:", serviceItem);
110
109
  const getAnimationIcon = (icon: string) => {
111
110
  const animation = ANIMATION_MAP[icon];
112
111
  if (!animation) return null;
@@ -615,7 +614,10 @@ function ServiceItemPB({
615
614
  />
616
615
  <div className="flex items-center mt-[2px]">
617
616
  <span className="bold-text ml-[6px]">
618
- {serviceItem?.offer_text || ""}&nbsp;
617
+ {(serviceItem?.offer_text || "").length > 30
618
+ ? (serviceItem?.offer_text || "").slice(0, 30) + "..."
619
+ : serviceItem?.offer_text || ""}
620
+ &nbsp;
619
621
  </span>{" "}
620
622
  | Termina en&nbsp;
621
623
  <span
@@ -651,7 +653,7 @@ function ServiceItemPB({
651
653
  {" "}
652
654
  {viewersConfig?.label || " viendo"} |{" "}
653
655
  <span className="">
654
- Quedan pocos{" "}
656
+ Quedan pocos.{" "}
655
657
  <span
656
658
  className="bold-text"
657
659
  ref={(node) =>
@@ -49,10 +49,27 @@ function ServiceItemMobile({
49
49
  const isItemExpanded = serviceItem.id === isExpanded;
50
50
  const isPetSeat = (Object.keys(serviceItem?.pet_seat_info) || []).length > 0;
51
51
  let isSoldOut = serviceItem.available_seats <= 0;
52
+
53
+ const seats = removeDuplicateSeats
54
+ ? serviceItem.seat_types?.filter(
55
+ (seat, index, self) =>
56
+ index === self.findIndex((s) => s.label === seat.label),
57
+ ) || []
58
+ : serviceItem.seat_types || [];
59
+
60
+ const discountedSeats = seats.map((seat) => ({
61
+ ...seat,
62
+ ...commonService.calculateDiscountedPrice(seat.fare, serviceItem as any),
63
+ }));
64
+
65
+ const hasDiscount = discountedSeats.some(
66
+ (seat) => seat.originalPrice !== seat.discountedPrice,
67
+ );
68
+
52
69
  const isLongOfferText = (serviceItem?.offer_text?.length || 0) > 35;
53
70
 
54
71
  const hasOfferText = Boolean(serviceItem?.offer_text);
55
- const offerGradient = "linear-gradient(90deg, #ff5964 0%, #ff8842 100%)";
72
+ const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
56
73
  const serviceCardStyle: React.CSSProperties = hasOfferText
57
74
  ? {
58
75
  borderColor: "transparent",
@@ -158,7 +175,10 @@ function ServiceItemMobile({
158
175
  <div className="flex justify-between items-center mb-[10px]">
159
176
  <div
160
177
  className="flex items-center justify-between"
161
- style={{ marginBottom: serviceItem?.offer_text ? "10px" : "" }}
178
+ style={{
179
+ marginBottom:
180
+ serviceItem?.offer_text || hasDiscount ? "10px" : "",
181
+ }}
162
182
  >
163
183
  <div className="w-[120px] overflow-y-hidden">
164
184
  <img
@@ -292,7 +312,7 @@ function ServiceItemMobile({
292
312
  >
293
313
  <div className="flex justify-between items-start">
294
314
  <div
295
- className={`flex ${isLongOfferText ? "items-start" : "items-center"}`}
315
+ className={`flex ${(serviceItem?.offer_text || "").length > 10 ? "items-start" : "items-center"}`}
296
316
  >
297
317
  <div className={isLongOfferText ? "mt-[2px]" : ""}>
298
318
  <LottiePlayer
@@ -309,9 +329,11 @@ function ServiceItemMobile({
309
329
  }}
310
330
  >
311
331
  {/* <span className="min-[380px]:text-[12px] bold-text">
312
- {serviceItem?.offer_text || ""}
313
- </span>{" "} */}
314
- {/* <span className="min-[380px]:text-[12px]">|</span>{" "} */}
332
+ {(serviceItem?.offer_text || "").length > 30
333
+ ? (serviceItem?.offer_text || "").slice(0, 30) + "..."
334
+ : serviceItem?.offer_text || ""}
335
+ </span>{" "}
336
+ <span className="min-[380px]:text-[12px]">|</span>{" "} */}
315
337
  <span className="whitespace-nowrap min-[380px]:text-[12px]">
316
338
  Termina en&nbsp;
317
339
  <span
@@ -358,7 +380,7 @@ function ServiceItemMobile({
358
380
  </div>
359
381
  <div className="flex items-center">
360
382
  <span className="whitespace-nowrap">
361
- Quedan pocos{" "}
383
+ Quedan pocos.{" "}
362
384
  <span
363
385
  className="bold-text"
364
386
  ref={(node) =>
@@ -166,6 +166,8 @@ export interface MobileServiceItemProps {
166
166
  lastSeatBg?: string;
167
167
 
168
168
  seatPriceColor?: string;
169
+ rightGradiantColor?: string;
170
+ leftGradiantColor?: string;
169
171
  };
170
172
  isCiva?: boolean;
171
173
  currencySign?: string;
@@ -284,8 +284,12 @@ function SeatSection({
284
284
  >
285
285
  {/* <span className="text-[18px] leading-[24px]">🔥</span> */}
286
286
  {renderIcon("fireIcon", "16px")}
287
- {currencySign || "$"}
288
- {Math.floor((discountSeat.discountedPrice / 1000) * 1000) / 1000}
287
+ {availableSeats <= 0
288
+ ? CommonService.currency(0, currencySign)
289
+ : CommonService.discountedCurrency(
290
+ discountSeat.discountedPrice,
291
+ currencySign,
292
+ )}
289
293
  </span>
290
294
  </div>
291
295
  </div>
@@ -169,16 +169,16 @@ function DateTimeSectionMobile({
169
169
  />
170
170
  )}
171
171
  </div>
172
- {isPeru ? null : (
173
- <div
174
- style={{
175
- width: "1px",
176
- height: "2.5rem",
177
- backgroundColor: "#ccc",
178
- margin: "auto",
179
- }}
180
- ></div>
181
- )}
172
+
173
+ <div
174
+ style={{
175
+ width: "1px",
176
+ height: "2.5rem",
177
+ backgroundColor: "#ccc",
178
+ margin: "auto",
179
+ }}
180
+ ></div>
181
+
182
182
  {/* SEATS */}
183
183
  <SeatSectionMobile
184
184
  seatTypes={seatTypes}
@@ -236,12 +236,24 @@ function SeatSectionMobile({
236
236
  ...commonService.calculateDiscountedPrice(seat.fare, serviceItem),
237
237
  }));
238
238
 
239
- const hasDiscount = discountedSeats?.some(
240
- (s) => s.originalPrice !== s.discountedPrice,
241
- );
242
- const discountSeat = discountedSeats
243
- ?.filter((seat) => !EXCEPTIONS.includes(seat.label))
244
- ?.sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
239
+ const peruLowestFare = isPeru ? getLowestFare() : null;
240
+ const peruDiscountCalc =
241
+ isPeru && peruLowestFare != null
242
+ ? commonService.calculateDiscountedPrice(peruLowestFare, serviceItem)
243
+ : null;
244
+
245
+ const hasDiscount = isPeru
246
+ ? peruDiscountCalc != null &&
247
+ peruDiscountCalc.originalPrice !== peruDiscountCalc.discountedPrice
248
+ : discountedSeats?.some((s) => s.originalPrice !== s.discountedPrice);
249
+
250
+ const discountSeat =
251
+ isPeru && peruDiscountCalc
252
+ ? { label: "", fare: peruLowestFare as number, ...peruDiscountCalc }
253
+ : discountedSeats
254
+ ?.filter((seat) => !EXCEPTIONS.includes(seat.label))
255
+ ?.sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
256
+
245
257
  const discountValue =
246
258
  serviceItem?.discount_type === "percentage" &&
247
259
  typeof serviceItem?.discount_value === "number"
@@ -273,8 +285,27 @@ function SeatSectionMobile({
273
285
  <span className="min-[420]:text-[13px] text-[12px] leading-[20px] text-[#c2c2c2]">
274
286
  Antes
275
287
  </span>
276
- <span className="min-[420]:text-[13px] text-[12px] leading-[20px] text-[#9f9f9f] line-through text-right">
288
+ <span
289
+ className="min-[420]:text-[13px] text-[12px] leading-[20px] text-[#9f9f9f] text-right"
290
+ style={{
291
+ position: "relative",
292
+ display: "inline-block",
293
+ overflow: "hidden",
294
+ }}
295
+ >
277
296
  {commonService.currency(discountSeat.originalPrice, currencySign)}
297
+ <span
298
+ style={{
299
+ position: "absolute",
300
+ top: "50%",
301
+ left: "25%",
302
+ width: "80%",
303
+ height: "1px",
304
+ background: "#9f9f9f",
305
+ transform: "rotate(-15deg)",
306
+ transformOrigin: "center",
307
+ }}
308
+ />
278
309
  </span>
279
310
 
280
311
  <span
@@ -295,7 +326,11 @@ function SeatSectionMobile({
295
326
  style={{ filter: isSoldOut ? "grayscale" : "" }}
296
327
  />
297
328
  ) : null}
298
- {commonService.currency(discountSeat.discountedPrice, currencySign)}
329
+ {/* {commonService.currency(discountSeat.discountedPrice, currencySign)} */}
330
+ {commonService.discountedCurrency(
331
+ discountSeat.discountedPrice,
332
+ currencySign,
333
+ )}
299
334
  </span>
300
335
 
301
336
  {isSoldOut ? (
@@ -8,6 +8,15 @@ const commonService = {
8
8
  return sign + formattedAmount;
9
9
  },
10
10
 
11
+ discountedCurrency(amount: number, currencySign?: string) {
12
+ const formattedAmount = Math.trunc(amount)
13
+ .toString()
14
+ .replace(/\B(?=(\d{3})+(?!\d))/g, ".");
15
+
16
+ const sign = currencySign || "$";
17
+ return sign + formattedAmount;
18
+ },
19
+
11
20
  copyObject: (ob) => {
12
21
  if (!ob) return {};
13
22
  return JSON.parse(JSON.stringify(ob));
@@ -344,24 +353,23 @@ const commonService = {
344
353
  const prevId = node.dataset.countdownId;
345
354
  if (prevId) clearInterval(Number(prevId));
346
355
 
347
- let remaining = countdownSeconds * 1000; // Convert to milliseconds
356
+ let remaining = countdownSeconds;
348
357
 
349
- const formatTime = (totalMs: number) => {
350
- const m = Math.floor(totalMs / 60000);
351
- const s = Math.floor((totalMs % 60000) / 1000);
352
- const ms = Math.floor((totalMs % 1000) / 10); // Show 2 digits for milliseconds
353
- return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}:${String(ms).padStart(2, "0")}`;
358
+ const formatTime = (totalSeconds: number) => {
359
+ const m = Math.floor(totalSeconds / 60);
360
+ const s = totalSeconds % 60;
361
+ return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
354
362
  };
355
363
 
356
364
  node.textContent = formatTime(remaining);
357
365
 
358
366
  const id = setInterval(() => {
359
- remaining -= 100; // Decrease by 100ms
367
+ remaining -= 1;
360
368
  if (remaining <= 0) {
361
- remaining = countdownSeconds * 1000;
369
+ remaining = countdownSeconds;
362
370
  }
363
371
  node.textContent = formatTime(remaining);
364
- }, 100); // Update every 100ms
372
+ }, 1000);
365
373
 
366
374
  node.dataset.countdownId = String(id);
367
375
  },