kupos-ui-components-lib 9.1.9 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,42 +1,43 @@
1
1
  import React from "react";
2
2
  import commonService from "../../utils/CommonService";
3
3
  import ServiceFilter from "./ServiceFilter";
4
+ let cachedStageOptions = null;
4
5
  const FilterBarDesktop = ({ t, serviceList, setServiceList, allSchedules, showReturnTripModal, filterArrData, isBlankTicket, operatorId, filtersArray, setFiltersArray, metaData, busTerminals, setTypes, colors, icons, translation, isTrain, isPeru, hideOperator, setCoachLoading, setCoachKey, isAllinBus, }) => {
5
6
  const getEnrichedFilters = () => {
6
- if (!isAllinBus || !(allSchedules === null || allSchedules === void 0 ? void 0 : allSchedules.length))
7
- return filtersArray;
8
7
  const hasSpecialDeparture = filtersArray.some((f) => { var _a; return f.type === "special_departure" && ((_a = f.options) === null || _a === void 0 ? void 0 : _a.length) > 0; });
9
8
  if (hasSpecialDeparture)
10
9
  return filtersArray;
11
- const stageMap = new Map();
12
- allSchedules.forEach((schedule) => {
13
- var _a, _b, _c;
14
- if ((_a = schedule.stage_details_arr) === null || _a === void 0 ? void 0 : _a.length) {
15
- const stageObj = schedule.stage_details_arr[0];
16
- const stageId = Object.keys(stageObj)[0];
17
- const stageName = (_c = (_b = stageObj[stageId]) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.split(", ")[0];
18
- if (stageId && stageName) {
19
- stageMap.set(stageId, stageName);
10
+ if (isAllinBus && (allSchedules === null || allSchedules === void 0 ? void 0 : allSchedules.length)) {
11
+ const stageMap = new Map();
12
+ allSchedules.forEach((schedule) => {
13
+ var _a, _b, _c;
14
+ if ((_a = schedule.stage_details_arr) === null || _a === void 0 ? void 0 : _a.length) {
15
+ const stageObj = schedule.stage_details_arr[0];
16
+ const stageId = Object.keys(stageObj)[0];
17
+ const stageName = (_c = (_b = stageObj[stageId]) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.split(", ")[0];
18
+ if (stageId && stageName) {
19
+ stageMap.set(stageId, stageName);
20
+ }
20
21
  }
22
+ });
23
+ if (stageMap.size > 0) {
24
+ cachedStageOptions = Array.from(stageMap.entries()).map(([id, name]) => ({
25
+ label: name,
26
+ value: id,
27
+ active: false,
28
+ }));
21
29
  }
22
- });
23
- if (stageMap.size === 0)
24
- return filtersArray;
25
- const specialDeptOptions = Array.from(stageMap.entries()).map(([id, name]) => ({
26
- label: name,
27
- value: id,
28
- active: false,
29
- }));
30
- return [
31
- ...filtersArray,
32
- {
33
- type: "special_departure",
34
- title: "SPECIAL_DEPARTURE",
35
- options: specialDeptOptions,
36
- },
37
- ];
30
+ }
31
+ if (cachedStageOptions === null || cachedStageOptions === void 0 ? void 0 : cachedStageOptions.length) {
32
+ return filtersArray.map((f) => f.type === "special_departure"
33
+ ? Object.assign(Object.assign({}, f), { options: cachedStageOptions }) : f);
34
+ }
35
+ return filtersArray;
38
36
  };
39
37
  const enrichedFilters = getEnrichedFilters();
38
+ if (enrichedFilters !== filtersArray) {
39
+ setFiltersArray(enrichedFilters);
40
+ }
40
41
  const filterBy = (filters) => {
41
42
  try {
42
43
  let routes = allSchedules;
@@ -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);
@@ -284,7 +283,11 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
284
283
  } },
285
284
  React.createElement("div", {
286
285
  // className="grid text-[#464647] w-full [grid-template-columns:18%_28%_2.5%_28%_15.5%] gap-x-[2%] items-center"
287
- className: "grid text-[#464647] w-full [grid-template-columns:22%_28%_2.5%_24%_15.5%] gap-x-[2%] items-center" },
286
+ className: "grid text-[#464647] w-full [grid-template-columns:22%_28%_2.5%_24%_15.5%] gap-x-[2%] items-center",
287
+ // style={{ marginTop: showTopLabel ? "8px" : "" }}
288
+ style: {
289
+ marginTop: showTopLabel && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ? "8px" : "",
290
+ } },
288
291
  React.createElement("div", { style: {
289
292
  display: "flex",
290
293
  flexDirection: "column",
@@ -323,7 +326,7 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
323
326
  zIndex: -1,
324
327
  } },
325
328
  React.createElement("div", { style: { overflow: "hidden", minHeight: 0, marginTop: "-10px" } },
326
- React.createElement(ExpandedDropdown, { serviceItem: serviceItem }))),
329
+ React.createElement(ExpandedDropdown, { serviceItem: serviceItem, isPeru: isPeru, translation: translation, getAnimationIcon: getAnimationIcon }))),
327
330
  children,
328
331
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && (React.createElement("div", { className: "text-white p-[10px_15px] text-left w-full flex items-center absolute -bottom-[36px] pt-[50px] -z-10 rounded-b-[14px] text-[14px]", style: {
329
332
  backgroundColor: colors === null || colors === void 0 ? void 0 : colors.bottomStripColor,
@@ -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);
@@ -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, serviceItem: serviceItem }),
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"),
@@ -228,6 +229,6 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
228
229
  zIndex: (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) ? -3 : undefined,
229
230
  marginTop: (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) ? "-15px" : "-10px",
230
231
  } },
231
- React.createElement(ExpandedDropdownMobile, { serviceItem: serviceItem })))));
232
+ React.createElement(ExpandedDropdownMobile, { serviceItem: serviceItem, isPeru: isPeru })))));
232
233
  }
233
234
  export default ServiceItemMobile;
package/dist/styles.css CHANGED
@@ -460,6 +460,9 @@
460
460
  .items-center {
461
461
  align-items: center;
462
462
  }
463
+ .items-start {
464
+ align-items: flex-start;
465
+ }
463
466
  .justify-between {
464
467
  justify-content: space-between;
465
468
  }
@@ -741,6 +744,9 @@
741
744
  .pb-\[7px\] {
742
745
  padding-bottom: 7px;
743
746
  }
747
+ .pb-\[8px\] {
748
+ padding-bottom: 8px;
749
+ }
744
750
  .pb-\[10px\] {
745
751
  padding-bottom: 10px;
746
752
  }
@@ -895,6 +901,10 @@
895
901
  --tw-duration: 300ms;
896
902
  transition-duration: 300ms;
897
903
  }
904
+ .outline-none {
905
+ --tw-outline-style: none;
906
+ outline-style: none;
907
+ }
898
908
  .group-hover\:block {
899
909
  &:is(:where(.group):hover *) {
900
910
  @media (hover: hover) {
@@ -6,6 +6,9 @@ interface ExpandedDropdownProps {
6
6
  is_change_ticket?: boolean;
7
7
  is_tracking_enabled?: boolean;
8
8
  };
9
+ isPeru?: boolean;
10
+ translation?: Record<string, string>;
11
+ getAnimationIcon?: (iconName: string) => any;
9
12
  }
10
- declare function ExpandedDropdown({ serviceItem, }: ExpandedDropdownProps): React.ReactElement;
13
+ declare function ExpandedDropdown({ serviceItem, isPeru, translation, getAnimationIcon, }: ExpandedDropdownProps): React.ReactElement;
11
14
  export default ExpandedDropdown;
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
- function ExpandedDropdown({ serviceItem, }) {
2
+ import LottiePlayer from "../../assets/LottiePlayer";
3
+ function ExpandedDropdown({ serviceItem, isPeru = false, translation = {}, getAnimationIcon, }) {
3
4
  const hasPetInfo = serviceItem.pet_seat_info &&
4
5
  Object.keys(serviceItem.pet_seat_info).length > 0;
5
6
  return (React.createElement("div", { className: "px-[15px] pt-[26px] pb-[14px] -mt-[16px] pt-[35px] relative -z-9", style: {
@@ -10,6 +11,10 @@ function ExpandedDropdown({ serviceItem, }) {
10
11
  // borderTop: "none",
11
12
  } },
12
13
  React.createElement("div", { className: "flex flex-col gap-[10px] text-[13px] text-[#464647]", style: { lineHeight: 1.6 } },
14
+ hasPetInfo && (React.createElement("div", { className: "flex items-center gap-[10px]" },
15
+ React.createElement(LottiePlayer, { animationData: getAnimationIcon("petFriendlyAnim"), width: "20px", height: "20px" }),
16
+ React.createElement("div", { className: "h-auto mr-[4px] text-[13px] text-[#464647] bold-text" },
17
+ React.createElement("span", null, translation === null || translation === void 0 ? void 0 : translation.petFriendly)))),
13
18
  React.createElement("div", { className: "flex gap-[8px] text-[13.33px]" },
14
19
  React.createElement("span", { style: { marginTop: "2px" } }, "\u2022"),
15
20
  React.createElement("span", null,
@@ -22,7 +27,7 @@ function ExpandedDropdown({ serviceItem, }) {
22
27
  " horas antes"),
23
28
  " ",
24
29
  "de la salida del bus. Al anular tu pasaje recibir\u00E1s una devoluci\u00F3n del 85% del monto de tu compra.")),
25
- React.createElement("div", { className: "flex gap-[8px]" },
30
+ isPeru ? null : (React.createElement("div", { className: "flex gap-[8px]" },
26
31
  React.createElement("span", { style: { marginTop: "2px" } }, "\u2022"),
27
32
  React.createElement("span", null,
28
33
  React.createElement("span", { className: "bold-text" }, "Pol\u00EDticas de cambios:"),
@@ -33,6 +38,6 @@ function ExpandedDropdown({ serviceItem, }) {
33
38
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.change_ticket_hours) || 6,
34
39
  " horas antes"),
35
40
  " ",
36
- "de la salida del bus. El monto ser\u00E1 reembolsado a tu billetera kupospay.")))));
41
+ "de la salida del bus. El monto ser\u00E1 reembolsado a tu billetera kupospay."))))));
37
42
  }
38
43
  export default ExpandedDropdown;
@@ -95,25 +95,25 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
95
95
  : null));
96
96
  });
97
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);
98
105
  const renderLabels = () => {
99
106
  if (isPeru) {
100
107
  return (React.createElement(React.Fragment, null,
101
- React.createElement("span", { className: "text-[13.33px]", style: {
108
+ hasDiscount && (React.createElement("span", { className: "text-[13.33px]", style: {
102
109
  color: "#999",
103
110
  // position: "relative",
104
111
  // bottom: numberOfSeats ? "10px" : "",
105
- } }, "Antes"),
112
+ } }, "Antes")),
106
113
  React.createElement("span", { className: "text-[13.33px]" }, "Desde")));
107
114
  }
108
115
  return renderSeatNames();
109
116
  };
110
- const strikethroughStyle = Object.assign({ color: "#ccc", display: "flex", textAlign: "end", textDecoration: "line-through" }, (isPeru
111
- ? { position: "relative", top: 0 }
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);
117
117
  return (React.createElement("div", { className: "relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]", style: isCentered ? { alignItems: "center" } : {} },
118
118
  React.createElement("div", { className: "flex flex-col justify-between", style: { gap: "10px" } }, renderLabels()),
119
119
  React.createElement("div", { className: "flex flex-col justify-between absolute inset-y-0 right-0 left-1/2 h-full", style: {
@@ -125,8 +125,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
125
125
  justifyContent: isCentered ? "center" : "",
126
126
  gap: "10px",
127
127
  } },
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))),
128
+ hasDiscount && !isPeru && (React.createElement("span", { className: "text-[13.33px]", style: strikethroughStyle }, formatPrice(highestOriginalPrice))),
130
129
  renderSeatPrices())));
131
130
  }
132
131
  export default SeatSection;
@@ -6,6 +6,7 @@ interface ExpandedDropdownMobileProps {
6
6
  is_change_ticket?: boolean;
7
7
  is_tracking_enabled?: boolean;
8
8
  };
9
+ isPeru?: boolean;
9
10
  }
10
- declare function ExpandedDropdownMobile({ serviceItem, }: ExpandedDropdownMobileProps): React.ReactElement;
11
+ declare function ExpandedDropdownMobile({ serviceItem, isPeru, }: ExpandedDropdownMobileProps): React.ReactElement;
11
12
  export default ExpandedDropdownMobile;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- function ExpandedDropdownMobile({ serviceItem, }) {
2
+ function ExpandedDropdownMobile({ serviceItem, isPeru, }) {
3
3
  return (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[12px] relative -z-9", style: {
4
4
  backgroundColor: "#ffefef",
5
5
  borderRadius: "0 0 14px 14px",
@@ -17,7 +17,7 @@ function ExpandedDropdownMobile({ serviceItem, }) {
17
17
  " horas antes"),
18
18
  " ",
19
19
  "de la salida del bus. Al anular tu pasaje recibir\u00E1s una devoluci\u00F3n del 85% del monto de tu compra.")),
20
- React.createElement("div", { className: "flex gap-[6px]" },
20
+ isPeru ? null : (React.createElement("div", { className: "flex gap-[6px]" },
21
21
  React.createElement("span", { style: { marginTop: "2px" } }, "\u2022"),
22
22
  React.createElement("span", null,
23
23
  React.createElement("span", { className: "bold-text" }, "Pol\u00EDticas de cambios:"),
@@ -28,6 +28,6 @@ function ExpandedDropdownMobile({ serviceItem, }) {
28
28
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.change_ticket_hours) || 6,
29
29
  " horas antes"),
30
30
  " ",
31
- "de la salida del bus. El monto ser\u00E1 reembolsado a tu billetera kupospay.")))));
31
+ "de la salida del bus. El monto ser\u00E1 reembolsado a tu billetera kupospay."))))));
32
32
  }
33
33
  export default ExpandedDropdownMobile;
@@ -70,11 +70,9 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
70
70
  const priceColor = isSoldOut ? "#bbb" : seatPriceColor;
71
71
  const { originalPrice, discountedPrice } = commonService.calculateDiscountedPrice(lowestFare, serviceItem);
72
72
  return (React.createElement(React.Fragment, null,
73
- 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" },
74
74
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px]", style: { color: "#bbb" } }, "Antes"),
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))),
75
+ React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] line-through", style: { color: "#bbb" } }, commonService.currency(originalPrice, currencySign)))),
78
76
  React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
79
77
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: isSoldOut ? "#bbb" : "#464647" } }, "Desde"),
80
78
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(discountedPrice, currencySign)))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "9.1.9",
3
+ "version": "9.2.0",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -3,6 +3,8 @@ import { FilterBarProps } from "./tyoes";
3
3
  import commonService from "../../utils/CommonService";
4
4
  import ServiceFilter from "./ServiceFilter";
5
5
 
6
+ let cachedStageOptions: any[] | null = null;
7
+
6
8
  const FilterBarDesktop = ({
7
9
  t,
8
10
  serviceList,
@@ -28,47 +30,52 @@ const FilterBarDesktop = ({
28
30
  isAllinBus,
29
31
  }: FilterBarProps) => {
30
32
  const getEnrichedFilters = () => {
31
- if (!isAllinBus || !allSchedules?.length) return filtersArray;
32
-
33
33
  const hasSpecialDeparture = filtersArray.some(
34
34
  (f: any) => f.type === "special_departure" && f.options?.length > 0,
35
35
  );
36
36
  if (hasSpecialDeparture) return filtersArray;
37
37
 
38
- const stageMap = new Map<string, string>();
39
- allSchedules.forEach((schedule: any) => {
40
- if (schedule.stage_details_arr?.length) {
41
- const stageObj = schedule.stage_details_arr[0];
42
- const stageId = Object.keys(stageObj)[0];
43
- const stageName = stageObj[stageId]?.[0]?.split(", ")[0];
44
- if (stageId && stageName) {
45
- stageMap.set(stageId, stageName);
38
+ if (isAllinBus && allSchedules?.length) {
39
+ const stageMap = new Map<string, string>();
40
+ allSchedules.forEach((schedule: any) => {
41
+ if (schedule.stage_details_arr?.length) {
42
+ const stageObj = schedule.stage_details_arr[0];
43
+ const stageId = Object.keys(stageObj)[0];
44
+ const stageName = stageObj[stageId]?.[0]?.split(", ")[0];
45
+ if (stageId && stageName) {
46
+ stageMap.set(stageId, stageName);
47
+ }
46
48
  }
47
- }
48
- });
49
+ });
49
50
 
50
- if (stageMap.size === 0) return filtersArray;
51
+ if (stageMap.size > 0) {
52
+ cachedStageOptions = Array.from(stageMap.entries()).map(
53
+ ([id, name]) => ({
54
+ label: name,
55
+ value: id,
56
+ active: false,
57
+ }),
58
+ );
59
+ }
60
+ }
51
61
 
52
- const specialDeptOptions = Array.from(stageMap.entries()).map(
53
- ([id, name]) => ({
54
- label: name,
55
- value: id,
56
- active: false,
57
- }),
58
- );
62
+ if (cachedStageOptions?.length) {
63
+ return filtersArray.map((f: any) =>
64
+ f.type === "special_departure"
65
+ ? { ...f, options: cachedStageOptions! }
66
+ : f,
67
+ );
68
+ }
59
69
 
60
- return [
61
- ...filtersArray,
62
- {
63
- type: "special_departure",
64
- title: "SPECIAL_DEPARTURE",
65
- options: specialDeptOptions,
66
- },
67
- ];
70
+ return filtersArray;
68
71
  };
69
72
 
70
73
  const enrichedFilters = getEnrichedFilters();
71
74
 
75
+ if (enrichedFilters !== filtersArray) {
76
+ setFiltersArray(enrichedFilters);
77
+ }
78
+
72
79
  const filterBy = (filters) => {
73
80
  try {
74
81
  let routes = allSchedules;
@@ -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);
@@ -497,6 +496,10 @@ function ServiceItemPB({
497
496
  // className="grid text-[#464647] w-full [grid-template-columns:18%_28%_2.5%_28%_15.5%] gap-x-[2%] items-center"
498
497
  className="grid text-[#464647] w-full [grid-template-columns:22%_28%_2.5%_24%_15.5%] gap-x-[2%] items-center"
499
498
  // style={{ marginTop: showTopLabel ? "8px" : "" }}
499
+ style={{
500
+ marginTop:
501
+ showTopLabel && serviceItem?.is_direct_trip ? "8px" : "",
502
+ }}
500
503
  >
501
504
  {/* OPERATOR LOGO */}
502
505
  <div
@@ -632,7 +635,12 @@ function ServiceItemPB({
632
635
  <div
633
636
  style={{ overflow: "hidden", minHeight: 0, marginTop: "-10px" }}
634
637
  >
635
- <ExpandedDropdown serviceItem={serviceItem} />
638
+ <ExpandedDropdown
639
+ serviceItem={serviceItem}
640
+ isPeru={isPeru}
641
+ translation={translation}
642
+ getAnimationIcon={getAnimationIcon}
643
+ />
636
644
  </div>
637
645
  </div>
638
646
 
@@ -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);
@@ -405,7 +406,7 @@ function ServiceItemMobile({
405
406
  {/* 🔹 EXPANDABLE DROPDOWN (below the card) */}
406
407
  {serviceItem?.offer_text && (
407
408
  <div
408
- 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]"
409
410
  style={{
410
411
  backgroundColor: isSoldOut ? "#ccc" : colors?.bottomStripColor,
411
412
  opacity: isSoldOut ? 0.5 : 1,
@@ -417,43 +418,49 @@ function ServiceItemMobile({
417
418
  className="flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]"
418
419
  style={{ lineHeight: 1.6 }}
419
420
  >
420
- <div className="flex flex-col">
421
- <div className="flex items-center">
422
- <LottiePlayer
423
- animationData={serviceItem.icons.bombAnim}
424
- width="18px"
425
- height="18px"
426
- />
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>
427
432
  <div
428
- className="ml-[6px] flex items-center mt-[2px]"
433
+ className={`ml-[6px] flex-1 outline-none ${isLongOfferText ? "mt-[2px]" : ""}`}
429
434
  style={{
430
435
  color: "#fff",
436
+ lineHeight: 1.4,
431
437
  }}
432
438
  >
433
- <span className=" min-[380px]:text-[12px]">
434
- {serviceItem?.offer_text || ""}&nbsp;
439
+ <span className="min-[380px]:text-[12px] bold-text">
440
+ {serviceItem?.offer_text || ""}
435
441
  </span>{" "}
436
- | Termina en&nbsp;
437
- <span
438
- className="bold-text"
439
- ref={startCountdown}
440
- style={{
441
- fontVariantNumeric: "tabular-nums",
442
- display: "inline-block",
443
- // minWidth: "70px",
444
- }}
445
- />
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>
446
454
  </div>
447
455
  </div>
448
456
  <div
449
- className="flex items-center"
457
+ className="flex items-start"
450
458
  style={{
451
459
  color: "#fff",
452
460
  }}
453
461
  >
454
- {renderIcon("personIcon", "16px")}
455
- &nbsp;
456
- <span className="">
462
+ <div>{renderIcon("personIcon", "16px")}</div>
463
+ <span className="flex-1" style={{ lineHeight: 1.4 }}>
457
464
  <span
458
465
  className="bold-text"
459
466
  ref={startViewerCount}
@@ -490,7 +497,7 @@ function ServiceItemMobile({
490
497
  marginTop: serviceItem?.offer_text ? "-15px" : "-10px",
491
498
  }}
492
499
  >
493
- <ExpandedDropdownMobile serviceItem={serviceItem} />
500
+ <ExpandedDropdownMobile serviceItem={serviceItem} isPeru={isPeru} />
494
501
  </div>
495
502
  </div>
496
503
  </div>
@@ -8,10 +8,16 @@ interface ExpandedDropdownProps {
8
8
  is_change_ticket?: boolean;
9
9
  is_tracking_enabled?: boolean;
10
10
  };
11
+ isPeru?: boolean;
12
+ translation?: Record<string, string>;
13
+ getAnimationIcon?: (iconName: string) => any;
11
14
  }
12
15
 
13
16
  function ExpandedDropdown({
14
17
  serviceItem,
18
+ isPeru = false,
19
+ translation = {},
20
+ getAnimationIcon,
15
21
  }: ExpandedDropdownProps): React.ReactElement {
16
22
  const hasPetInfo =
17
23
  serviceItem.pet_seat_info &&
@@ -71,6 +77,18 @@ function ExpandedDropdown({
71
77
  className="flex flex-col gap-[10px] text-[13px] text-[#464647]"
72
78
  style={{ lineHeight: 1.6 }}
73
79
  >
80
+ {hasPetInfo && (
81
+ <div className="flex items-center gap-[10px]">
82
+ <LottiePlayer
83
+ animationData={getAnimationIcon("petFriendlyAnim")}
84
+ width="20px"
85
+ height="20px"
86
+ />
87
+ <div className="h-auto mr-[4px] text-[13px] text-[#464647] bold-text">
88
+ <span>{translation?.petFriendly}</span>
89
+ </div>
90
+ </div>
91
+ )}
74
92
  <div className="flex gap-[8px] text-[13.33px]">
75
93
  <span style={{ marginTop: "2px" }}>•</span>
76
94
  <span>
@@ -83,18 +101,20 @@ function ExpandedDropdown({
83
101
  del 85% del monto de tu compra.
84
102
  </span>
85
103
  </div>
86
- <div className="flex gap-[8px]">
87
- <span style={{ marginTop: "2px" }}>•</span>
88
- <span>
89
- <span className="bold-text">Políticas de cambios:</span> Tu pasaje
90
- puede ser cambiado de manera online{" "}
91
- <span className="bold-text">
92
- hasta {serviceItem?.change_ticket_hours || 6} horas antes
93
- </span>{" "}
94
- de la salida del bus. El monto será reembolsado a tu billetera
95
- kupospay.
96
- </span>
97
- </div>
104
+ {isPeru ? null : (
105
+ <div className="flex gap-[8px]">
106
+ <span style={{ marginTop: "2px" }}>•</span>
107
+ <span>
108
+ <span className="bold-text">Políticas de cambios:</span> Tu pasaje
109
+ puede ser cambiado de manera online{" "}
110
+ <span className="bold-text">
111
+ hasta {serviceItem?.change_ticket_hours || 6} horas antes
112
+ </span>{" "}
113
+ de la salida del bus. El monto será reembolsado a tu billetera
114
+ kupospay.
115
+ </span>
116
+ </div>
117
+ )}
98
118
  </div>
99
119
  </div>
100
120
  );
@@ -178,27 +178,6 @@ function SeatSection({
178
178
  });
179
179
  };
180
180
 
181
- const renderLabels = () => {
182
- if (isPeru) {
183
- return (
184
- <>
185
- <span
186
- className="text-[13.33px]"
187
- style={{
188
- color: "#999",
189
- // position: "relative",
190
- // bottom: numberOfSeats ? "10px" : "",
191
- }}
192
- >
193
- Antes
194
- </span>
195
- <span className="text-[13.33px]">Desde</span>
196
- </>
197
- );
198
- }
199
- return renderSeatNames();
200
- };
201
-
202
181
  const strikethroughStyle: React.CSSProperties = {
203
182
  color: "#ccc",
204
183
  display: "flex",
@@ -223,6 +202,29 @@ function SeatSection({
223
202
  (seat) => seat.originalPrice !== seat.discountedPrice,
224
203
  );
225
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
+
226
228
  return (
227
229
  <div
228
230
  className="relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]"
@@ -243,13 +245,13 @@ function SeatSection({
243
245
  gap: "10px",
244
246
  }}
245
247
  >
246
- {isPeru && (
248
+ {/* {isPeru && (
247
249
  <span className="text-[13.33px]" style={strikethroughStyle}>
248
250
  {formatPrice(1000)}
249
251
  </span>
250
- )}
252
+ )} */}
251
253
 
252
- {hasDiscount && (
254
+ {hasDiscount && !isPeru && (
253
255
  <span className="text-[13.33px]" style={strikethroughStyle}>
254
256
  {formatPrice(highestOriginalPrice)}
255
257
  </span>
@@ -7,10 +7,12 @@ interface ExpandedDropdownMobileProps {
7
7
  is_change_ticket?: boolean;
8
8
  is_tracking_enabled?: boolean;
9
9
  };
10
+ isPeru?: boolean;
10
11
  }
11
12
 
12
13
  function ExpandedDropdownMobile({
13
14
  serviceItem,
15
+ isPeru,
14
16
  }: ExpandedDropdownMobileProps): React.ReactElement {
15
17
  return (
16
18
  <div
@@ -36,18 +38,20 @@ function ExpandedDropdownMobile({
36
38
  del 85% del monto de tu compra.
37
39
  </span>
38
40
  </div>
39
- <div className="flex gap-[6px]">
40
- <span style={{ marginTop: "2px" }}>•</span>
41
- <span>
42
- <span className="bold-text">Políticas de cambios:</span> Tu pasaje
43
- puede ser cambiado de manera online{" "}
44
- <span className="bold-text">
45
- hasta {serviceItem?.change_ticket_hours || 6} horas antes
46
- </span>{" "}
47
- de la salida del bus. El monto será reembolsado a tu billetera
48
- kupospay.
49
- </span>
50
- </div>
41
+ {isPeru ? null : (
42
+ <div className="flex gap-[6px]">
43
+ <span style={{ marginTop: "2px" }}>•</span>
44
+ <span>
45
+ <span className="bold-text">Políticas de cambios:</span> Tu pasaje
46
+ puede ser cambiado de manera online{" "}
47
+ <span className="bold-text">
48
+ hasta {serviceItem?.change_ticket_hours || 6} horas antes
49
+ </span>{" "}
50
+ de la salida del bus. El monto será reembolsado a tu billetera
51
+ kupospay.
52
+ </span>
53
+ </div>
54
+ )}
51
55
  </div>
52
56
  </div>
53
57
  );
@@ -155,22 +155,22 @@ function SeatSectionMobile({
155
155
 
156
156
  return (
157
157
  <>
158
- <div className="w-[100%] flex flex-row justify-between items-center">
159
- <span
160
- className="min-[420]:text-[13px] text-[12px]"
161
- style={{ color: "#bbb" }}
162
- >
163
- Antes
164
- </span>
165
- <span
166
- className="min-[420]:text-[13px] text-[12px] line-through"
167
- style={{ color: "#bbb" }}
168
- >
169
- {originalPrice !== discountedPrice
170
- ? commonService.currency(originalPrice, currencySign)
171
- : commonService.currency(getHighestFare(), currencySign)}
172
- </span>
173
- </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
+ )}
174
174
  <div className="w-[100%] flex flex-row justify-between items-center">
175
175
  <span
176
176
  className="min-[420]:text-[13px] text-[12px] bold-text"