kupos-ui-components-lib 9.9.3 → 9.9.4

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,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { ServiceItemProps } from "./types";
3
- declare 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, siteType, isAllinBus, isExpand, setIsExpand, coachKey, viewersConfig, showLoginModal, isLoggedIn, showLoginOption, isFlores, operatorLabel, }: ServiceItemProps & {
3
+ declare 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, siteType, isAllinBus, isExpand, setIsExpand, coachKey, viewersConfig, showLoginModal, isLoggedIn, showLoginOption, isTrain, selectedSeatKey, onSeatSelect, onTrainButtonClick, showSeatSelectionError, onShowSeatSelectionError, onClearSeatSelectionError, isFlores, operatorLabel, }: ServiceItemProps & {
4
4
  currencySign?: string;
5
5
  }): React.ReactElement;
6
6
  export default ServiceItemPB;
@@ -74,8 +74,12 @@ const ANIMATION_MAP = {
74
74
  kupos: femaleAnimation,
75
75
  },
76
76
  };
77
- 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, showLoginModal, isLoggedIn, showLoginOption, isFlores, operatorLabel, }) {
77
+ 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, showLoginModal, isLoggedIn, showLoginOption, isTrain, selectedSeatKey, onSeatSelect, onTrainButtonClick, showSeatSelectionError, onShowSeatSelectionError, onClearSeatSelectionError, isFlores, operatorLabel, }) {
78
78
  var _a;
79
+ const handleSeatSelect = (key, price, seatKey, apiSeatType) => {
80
+ onClearSeatSelectionError === null || onClearSeatSelectionError === void 0 ? void 0 : onClearSeatSelectionError();
81
+ onSeatSelect === null || onSeatSelect === void 0 ? void 0 : onSeatSelect(key, price, seatKey, apiSeatType);
82
+ };
79
83
  const getAnimationIcon = (icon) => {
80
84
  var _a;
81
85
  const animation = ANIMATION_MAP[icon];
@@ -213,6 +217,16 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
213
217
  });
214
218
  return;
215
219
  }
220
+ if (isTrain) {
221
+ if (!selectedSeatKey) {
222
+ onShowSeatSelectionError === null || onShowSeatSelectionError === void 0 ? void 0 : onShowSeatSelectionError(serviceItem.id);
223
+ return;
224
+ }
225
+ if (onTrainButtonClick) {
226
+ onTrainButtonClick();
227
+ return;
228
+ }
229
+ }
216
230
  onBookButtonPress();
217
231
  };
218
232
  const items = [
@@ -275,9 +289,13 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
275
289
  backgroundColor: "#ccc",
276
290
  } }),
277
291
  React.createElement("div", { className: "content-center" },
278
- React.createElement(SeatSection, { seatTypes: serviceItem.seat_types, serviceItem: serviceItem, availableSeats: serviceItem.available_seats, isSoldOut: isSoldOut, priceColor: colors.priceColor, dpSeatColor: colors.seatPriceColor, currencySign: currencySign, removeDuplicateSeats: removeDuplicateSeats, isPeru: isPeru, renderIcon: renderIcon, discountSeatPriceColor: colors.discountSeatPriceColor })),
292
+ React.createElement(SeatSection, { seatTypes: serviceItem.seat_types, serviceItem: serviceItem, availableSeats: serviceItem.available_seats, isSoldOut: isSoldOut, priceColor: colors.priceColor, dpSeatColor: colors.seatPriceColor, currencySign: currencySign, removeDuplicateSeats: removeDuplicateSeats, isPeru: isPeru, renderIcon: renderIcon, discountSeatPriceColor: colors.discountSeatPriceColor, isTrain: isTrain, selectedSeatKey: selectedSeatKey, onSeatSelect: handleSeatSelect, topLabelColor: colors.topLabelColor })),
279
293
  React.createElement("div", { className: "relative" },
280
294
  React.createElement(KuposButton, { isSoldOut: isSoldOut, isLoading: serviceDetailsLoading, buttonColor: colors.kuposButtonColor, buyLabel: translation === null || translation === void 0 ? void 0 : translation.buyButton, soldOutLabel: translation === null || translation === void 0 ? void 0 : translation.soldOutButton, soldOutIcon: renderIcon("soldOutIcon", "14px"), onClick: checkMidnight }),
295
+ showSeatSelectionError === serviceItem.id && isTrain && (React.createElement("div", { className: "flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]" },
296
+ React.createElement("div", { className: "text-[9px] text-center whitespace-nowrap", style: {
297
+ color: colors.seatPriceColor,
298
+ } }, "Selecciona el tipo de servicio"))),
281
299
  showLastSeats ? (React.createElement("div", { className: "flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]" }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) < 10 &&
282
300
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) > 0 && (React.createElement("div", { className: "text-[12px] mt-1 text-center", style: {
283
301
  color: colors.seatPriceColor,
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
2
  import { MobileServiceItemProps } from "./mobileTypes";
3
- declare function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, operatorLabel, }: MobileServiceItemProps): React.ReactElement;
3
+ declare function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, operatorLabel, isTrain, }: MobileServiceItemProps): React.ReactElement;
4
4
  export default ServiceItemMobile;
@@ -17,7 +17,7 @@ const exceptions = [
17
17
  "blanco",
18
18
  "asiento_mascota",
19
19
  ];
20
- function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, operatorLabel, }) {
20
+ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, operatorLabel, isTrain, }) {
21
21
  var _a, _b, _c, _d, _e, _f, _g;
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;
@@ -110,7 +110,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
110
110
  React.createElement("span", { style: { lineHeight: "normal" } }, getServiceStars(serviceItem))),
111
111
  React.createElement("div", { className: "flex items-center cursor-pointer ", style: { color: isSoldOut ? "#bbb" : "text-[#464647]" } },
112
112
  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)),
113
- 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, showLastSeats: showLastSeats, discountSeatPriceColor: colors.discountSeatPriceColor }),
113
+ 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, showLastSeats: showLastSeats, discountSeatPriceColor: colors.discountSeatPriceColor, isTrain: isTrain }),
114
114
  hasDiscount && (React.createElement("div", { className: "flex justify-end" }, isSoldOut ? (React.createElement("span", { className: "col-span-2 min-[420]:text-[13px] text-right text-[12px] text-[#ccc]" }, "Agotado")) : null)),
115
115
  showLastSeats ? (React.createElement("div", { className: "flex justify-end " }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) < 10 &&
116
116
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) > 0 && (React.createElement("div", { className: "text-[10px] text-center", style: {
@@ -188,4 +188,5 @@ export interface MobileServiceItemProps {
188
188
  icon?: string;
189
189
  };
190
190
  operatorLabel?: string;
191
+ isTrain?: boolean;
191
192
  }
@@ -224,6 +224,13 @@ export interface ServiceItemProps {
224
224
  showLoginModal?: any;
225
225
  isLoggedIn?: any;
226
226
  showLoginOption?: boolean;
227
+ isTrain?: boolean;
228
+ selectedSeatKey?: any;
229
+ onSeatSelect?: (key: any, price: number, seatKey: string, apiSeatType?: string) => void;
230
+ onTrainButtonClick?: any;
231
+ showSeatSelectionError?: string | null;
232
+ onShowSeatSelectionError?: (serviceId: string) => void;
233
+ onClearSeatSelectionError?: () => void;
227
234
  isFlores?: boolean;
228
235
  operatorLabel?: string;
229
236
  }
package/dist/styles.css CHANGED
@@ -863,6 +863,9 @@
863
863
  .text-right {
864
864
  text-align: right;
865
865
  }
866
+ .text-\[9px\] {
867
+ font-size: 9px;
868
+ }
866
869
  .text-\[10px\] {
867
870
  font-size: 10px;
868
871
  }
@@ -3,6 +3,8 @@ interface SeatType {
3
3
  label: string;
4
4
  fare: number;
5
5
  key: any;
6
+ apiSeatType?: string;
7
+ api_seat_type?: string;
6
8
  }
7
9
  interface SeatSectionProps {
8
10
  seatTypes: SeatType[];
@@ -16,6 +18,10 @@ interface SeatSectionProps {
16
18
  serviceItem?: any;
17
19
  renderIcon?: (iconKey: string, size?: string) => React.ReactNode;
18
20
  discountSeatPriceColor?: string;
21
+ isTrain?: boolean;
22
+ selectedSeatKey?: any;
23
+ onSeatSelect?: (key: any, price: number, seatKey: string, apiSeatType?: string) => void;
24
+ topLabelColor?: string;
19
25
  }
20
- declare function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, }: SeatSectionProps): React.ReactElement;
26
+ declare function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, selectedSeatKey, onSeatSelect, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, isTrain, topLabelColor, }: SeatSectionProps): React.ReactElement;
21
27
  export default SeatSection;
@@ -8,11 +8,13 @@ function getAllSeatTypes(seatTypes) {
8
8
  let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
9
9
  label: val === null || val === void 0 ? void 0 : val.label,
10
10
  price: val === null || val === void 0 ? void 0 : val.fare,
11
+ key: val === null || val === void 0 ? void 0 : val.key,
12
+ apiSeatType: (val === null || val === void 0 ? void 0 : val.apiSeatType) || (val === null || val === void 0 ? void 0 : val.api_seat_type),
11
13
  }));
12
14
  seatTypesWithPrices.sort((a, b) => a.price - b.price);
13
15
  return seatTypesWithPrices;
14
16
  }
15
- function getSortedSeatTypes(seatTypes) {
17
+ function getSortedSeatTypes(seatTypes, isTrain) {
16
18
  if (!(seatTypes === null || seatTypes === void 0 ? void 0 : seatTypes.length)) {
17
19
  return [{ label: "Salon cama", price: 0 }];
18
20
  }
@@ -21,7 +23,9 @@ function getSortedSeatTypes(seatTypes) {
21
23
  if (premiumIndex >= 3) {
22
24
  seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
23
25
  }
24
- seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
26
+ if (!isTrain) {
27
+ seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
28
+ }
25
29
  const seenPrices = new Set();
26
30
  seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
27
31
  if (seenPrices.has(seat.price))
@@ -54,10 +58,10 @@ function getUniqueSeats(seatTypes) {
54
58
  function getNumberOfSeats(seatTypes) {
55
59
  return seatTypes.filter((val) => !SEAT_EXCEPTIONS.includes(val.label)).length;
56
60
  }
57
- function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, }) {
61
+ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, selectedSeatKey, onSeatSelect, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, isTrain, topLabelColor, }) {
58
62
  var _a;
59
63
  const uniqueSeats = getUniqueSeats(seatTypes);
60
- const sortedSeatTypes = getSortedSeatTypes(seatTypes);
64
+ const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
61
65
  const numberOfSeats = getNumberOfSeats(seatTypes);
62
66
  const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
63
67
  const formatPrice = (price) => availableSeats <= 0
@@ -65,11 +69,36 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
65
69
  : CommonService.currency(price, currencySign);
66
70
  const renderSeatNames = () => {
67
71
  const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
68
- return seats.map((val, key) => SEAT_EXCEPTIONS.includes(val.label) ? null : (React.createElement("span", { key: key, className: `flex items-center justify-between text-[13.33px] ${isSoldOut ? "text-[#c0c0c0]" : ""}` }, typeof val.label === "string" || typeof val.label === "number"
69
- ? removeDuplicateSeats && isPeru
70
- ? CommonService.truncateSeatLabel(val.label)
71
- : val.label
72
- : null)));
72
+ return seats.map((val, key) => {
73
+ return SEAT_EXCEPTIONS.includes(val.label) ? null : (React.createElement("div", { className: "flex items-center", style: isTrain ? { cursor: "pointer" } : undefined, onClick: isTrain && !isSoldOut
74
+ ? () => val.label === selectedSeatKey
75
+ ? onSeatSelect === null || onSeatSelect === void 0 ? void 0 : onSeatSelect(null, 0, "", "")
76
+ : onSeatSelect === null || onSeatSelect === void 0 ? void 0 : onSeatSelect(val.label, val.price, val.key, val.apiSeatType)
77
+ : undefined },
78
+ isTrain && (React.createElement("div", { style: {
79
+ border: `1px solid ${val.label === selectedSeatKey ? topLabelColor : "#ccc"}`,
80
+ borderRadius: "50%",
81
+ width: "14px",
82
+ height: "14px",
83
+ minWidth: "14px",
84
+ marginRight: "10px",
85
+ display: "flex",
86
+ alignItems: "center",
87
+ justifyContent: "center",
88
+ } }, val.label === selectedSeatKey && (React.createElement("div", { style: {
89
+ backgroundColor: topLabelColor,
90
+ borderRadius: "50%",
91
+ width: "7px",
92
+ height: "7px",
93
+ } })))),
94
+ React.createElement("span", { key: key, className: `flex items-center justify-between text-[13.33px] ${isSoldOut ? "text-[#c0c0c0]" : ""}` }, typeof val.label === "string" || typeof val.label === "number"
95
+ ? removeDuplicateSeats && isPeru
96
+ ? CommonService.truncateSeatLabel(val.label)
97
+ : isTrain
98
+ ? CommonService.truncateSeatLabel(val.label, 8)
99
+ : val.label
100
+ : null)));
101
+ });
73
102
  };
74
103
  const renderSeatPrices = () => {
75
104
  if (isPeru) {
@@ -22,6 +22,7 @@ interface DateTimeSectionMobileProps {
22
22
  tooltipBgColor?: string;
23
23
  showLastSeats?: boolean;
24
24
  discountSeatPriceColor?: string;
25
+ isTrain?: boolean;
25
26
  }
26
- declare function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, }: DateTimeSectionMobileProps): React.ReactElement;
27
+ declare function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, isTrain, }: DateTimeSectionMobileProps): React.ReactElement;
27
28
  export default DateTimeSectionMobile;
@@ -23,8 +23,10 @@ const getCleanedDepTime = (raw) => {
23
23
  };
24
24
  const TimeRow = ({ label, icon, alt, date, timeContent, isSoldOut, }) => {
25
25
  const formattedDate = DateService.getServiceItemDate(date);
26
- const dotPositionClass = formattedDate.includes("dom") ? "max-[399px]:left-[53%]" : "";
27
- return React.createElement("div", { className: `flex items-center min-[420]:text-[13px] text-[12px] justify-between ${isSoldOut ? "text-[#c0c0c0]" : ""}` },
26
+ const dotPositionClass = formattedDate.includes("dom")
27
+ ? "max-[399px]:left-[53%]"
28
+ : "";
29
+ return (React.createElement("div", { className: `flex items-center min-[420]:text-[13px] text-[12px] justify-between ${isSoldOut ? "text-[#c0c0c0]" : ""}` },
28
30
  React.createElement("div", { className: "flex items-center", style: { flex: 1 } },
29
31
  React.createElement("div", null,
30
32
  " ",
@@ -33,9 +35,9 @@ const TimeRow = ({ label, icon, alt, date, timeContent, isSoldOut, }) => {
33
35
  React.createElement("div", { className: "flex items-center relative capitalize justify-between", style: { flex: 1 } },
34
36
  React.createElement("span", { className: "cursor-pointer black-text" }, formattedDate),
35
37
  React.createElement("div", { className: `absolute left-[50%] ${dotPositionClass}` }, "\u2022"),
36
- React.createElement("div", { className: "font-[900] relative black-text" }, timeContent))));
38
+ React.createElement("div", { className: "font-[900] relative black-text" }, timeContent)))));
37
39
  };
38
- function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, }) {
40
+ function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal, isPeru, orignLabel, destinationLabel, originIcon, destinationIcon, travelDate, arrivalDate, depTime, arrTime, seatTypes, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, isTrain, }) {
39
41
  const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
40
42
  const depTimeContent = isLinatal ? (React.createElement("div", null,
41
43
  React.createElement("span", null,
@@ -56,6 +58,6 @@ function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal
56
58
  backgroundColor: "#ccc",
57
59
  margin: "auto",
58
60
  } }),
59
- React.createElement(SeatSectionMobile, { seatTypes: seatTypes, isSoldOut: isSoldOut, isPeru: isPeru, seatPriceColor: seatPriceColor, currencySign: currencySign, availableSeats: availableSeats, removeDuplicateSeats: removeDuplicateSeats, serviceItem: serviceItem, tooltipBgColor: tooltipBgColor, showLastSeats: showLastSeats, discountSeatPriceColor: discountSeatPriceColor })));
61
+ React.createElement(SeatSectionMobile, { seatTypes: seatTypes, isSoldOut: isSoldOut, isPeru: isPeru, seatPriceColor: seatPriceColor, currencySign: currencySign, availableSeats: availableSeats, removeDuplicateSeats: removeDuplicateSeats, serviceItem: serviceItem, tooltipBgColor: tooltipBgColor, showLastSeats: showLastSeats, discountSeatPriceColor: discountSeatPriceColor, isTrain: isTrain })));
60
62
  }
61
63
  export default DateTimeSectionMobile;
@@ -16,6 +16,7 @@ interface SeatSectionMobileProps {
16
16
  tooltipBgColor?: string;
17
17
  showLastSeats?: boolean;
18
18
  discountSeatPriceColor?: string;
19
+ isTrain?: boolean;
19
20
  }
20
- declare function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, }: SeatSectionMobileProps): React.ReactElement;
21
+ declare function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, isTrain, }: SeatSectionMobileProps): React.ReactElement;
21
22
  export default SeatSectionMobile;
@@ -42,8 +42,8 @@ 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, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, }) {
46
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
45
+ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, isTrain, }) {
46
+ var _a, _b, _c, _d, _e, _f, _g;
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) {
@@ -101,7 +101,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
101
101
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] text-[#464647]", style: { opacity: isSoldOut ? 0.5 : 1 } }, "Agotado"))) : null));
102
102
  };
103
103
  const renderSeats = () => {
104
- var _a, _b, _c;
104
+ var _a, _b;
105
105
  if (isPeru) {
106
106
  return renderPeruSeats();
107
107
  }
@@ -109,11 +109,16 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
109
109
  const uniqueSeats = getUniqueSeats(seatTypesData, 3);
110
110
  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]" })));
111
111
  }
112
- 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]" })));
112
+ const filteredSeats = (_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);
113
+ return (_b = (isTrain ? filteredSeats : filteredSeats === null || filteredSeats === void 0 ? void 0 : filteredSeats.slice(0, 2))) === null || _b === void 0 ? void 0 : _b.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]" })));
113
114
  };
114
115
  const seats = removeDuplicateSeats
115
116
  ? getUniqueSeats(seatTypesData, 3)
116
- : (_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);
117
+ : (() => {
118
+ var _a;
119
+ const filtered = (_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);
120
+ return isTrain ? filtered : filtered === null || filtered === void 0 ? void 0 : filtered.slice(0, 2);
121
+ })();
117
122
  const discountedSeats = seats === null || seats === void 0 ? void 0 : seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
118
123
  const peruLowestFare = isPeru ? getLowestFare() : null;
119
124
  const peruDiscountCalc = isPeru && peruLowestFare != null
@@ -124,7 +129,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
124
129
  peruDiscountCalc.originalPrice !== peruDiscountCalc.discountedPrice
125
130
  : discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
126
131
  const discountSeat = isPeru && peruDiscountCalc
127
- ? 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];
132
+ ? Object.assign({ label: "", fare: peruLowestFare }, peruDiscountCalc) : (_b = discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.filter((seat) => !EXCEPTIONS.includes(seat.label))) === null || _b === void 0 ? void 0 : _b.sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
128
133
  const discountValue = (() => {
129
134
  if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_type) === "percentage" &&
130
135
  typeof (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) === "number") {
@@ -147,14 +152,14 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
147
152
  };
148
153
  const originalDpPrice = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.original_dp_price);
149
154
  const dpDiscountPercent = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents);
150
- const firstSeatFare = (_f = (_e = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label) && !EXCEPTIONS.includes(item.label))) === null || _e === void 0 ? void 0 : _e.sort((a, b) => a.fare - b.fare)[0]) === null || _f === void 0 ? void 0 : _f.fare;
155
+ const firstSeatFare = (_d = (_c = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label) && !EXCEPTIONS.includes(item.label))) === null || _c === void 0 ? void 0 : _c.sort((a, b) => a.fare - b.fare)[0]) === null || _d === void 0 ? void 0 : _d.fare;
151
156
  const hasDpDiscount = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) &&
152
157
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) &&
153
158
  originalDpPrice != null &&
154
159
  dpDiscountPercent != null &&
155
160
  firstSeatFare != null;
156
161
  return (React.createElement("div", { className: "content-center relative", style: { width: "40%" } }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
157
- !((_g = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _g === void 0 ? void 0 : _g.length) &&
162
+ !((_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _e === void 0 ? void 0 : _e.length) &&
158
163
  !dpDiscountPercent ? (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem]", style: { gap: isSoldOut ? "0px" : "5px" } }, renderDpSeats())) : hasDpDiscount ? (React.createElement("div", { className: "relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] " },
159
164
  !isNaN(Number(dpDiscountPercent)) &&
160
165
  Number(dpDiscountPercent) > 0 && (React.createElement("div", { className: "absolute -top-[18px] right-[0px]", style: {
@@ -186,7 +191,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
186
191
  React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
187
192
  color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
188
193
  } },
189
- ((_h = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _h === void 0 ? void 0 : _h.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[16px] w-[16px] object-contain", style: { filter: isSoldOut ? "grayscale" : "" } })) : null,
194
+ ((_f = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _f === void 0 ? void 0 : _f.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[16px] w-[16px] object-contain", style: { filter: isSoldOut ? "grayscale" : "" } })) : null,
190
195
  commonService.discountedCurrency(Number(firstSeatFare), currencySign)),
191
196
  isSoldOut ? (React.createElement("span", { className: "col-span-2 min-[420]:text-[13px] text-right text-[12px] text-[#ccc]" }, "Agotado")) : null)) : hasDiscount && discountSeat ? (React.createElement("div", null,
192
197
  React.createElement("div", { className: "relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] " },
@@ -220,7 +225,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
220
225
  React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
221
226
  color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
222
227
  } },
223
- ((_j = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _j === void 0 ? void 0 : _j.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[16px] w-[16px] object-contain", style: { opacity: isSoldOut ? 0.5 : 1 } })) : null,
228
+ ((_g = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _g === void 0 ? void 0 : _g.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[16px] w-[16px] object-contain", style: { opacity: isSoldOut ? 0.5 : 1 } })) : null,
224
229
  commonService.discountedCurrency(discountSeat.discountedPrice, currencySign))))) : (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem] ", style: {
225
230
  gap: isSoldOut ? "0px" : "5px",
226
231
  justifyContent: hasMultipleTypes ? "space-between" : "center",
@@ -3,7 +3,7 @@ declare const commonService: {
3
3
  discountedCurrency(amount: number, currencySign?: string): string;
4
4
  copyObject: (ob: any) => any;
5
5
  getServiceTypeLabelForFilters: (service_type: any) => "Tipo de servicio" | "Punto de embarque" | "Tipo de asiento" | "SERVICIOS" | "";
6
- truncateSeatLabel: (label: string | number) => string;
6
+ truncateSeatLabel: (label: string | number, maxLength?: number) => string;
7
7
  getAmenitiesImage: (name: string, serviceItem: any) => string;
8
8
  getAmenityName: (rawAmenity: string) => string;
9
9
  getSeatNameForFilters: (rawSeat: any) => any;
@@ -34,11 +34,15 @@ const commonService = {
34
34
  return "";
35
35
  }
36
36
  },
37
- truncateSeatLabel: (label) => {
37
+ truncateSeatLabel: (label, maxLength) => {
38
38
  if (typeof label !== "string")
39
39
  return String(label);
40
40
  if (label.includes("("))
41
41
  return label;
42
+ // If maxLength provided, hard-truncate regardless of word count
43
+ if (maxLength != null && label.length > maxLength) {
44
+ return label.slice(0, maxLength) + "...";
45
+ }
42
46
  const words = label.trim().split(/\s+/);
43
47
  const truncateWord = (word) => word.length > 5 ? word.slice(0, 3) + "..." : word;
44
48
  if (words.length === 1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "9.9.3",
3
+ "version": "9.9.4",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -116,9 +116,25 @@ function ServiceItemPB({
116
116
  showLoginModal,
117
117
  isLoggedIn,
118
118
  showLoginOption,
119
+ isTrain,
120
+ selectedSeatKey,
121
+ onSeatSelect,
122
+ onTrainButtonClick,
123
+ showSeatSelectionError,
124
+ onShowSeatSelectionError,
125
+ onClearSeatSelectionError,
119
126
  isFlores,
120
127
  operatorLabel,
121
128
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
129
+ const handleSeatSelect = (
130
+ key: any,
131
+ price: number,
132
+ seatKey: string,
133
+ apiSeatType?: string,
134
+ ) => {
135
+ onClearSeatSelectionError?.();
136
+ onSeatSelect?.(key, price, seatKey, apiSeatType);
137
+ };
122
138
  const getAnimationIcon = (icon: string) => {
123
139
  const animation = ANIMATION_MAP[icon];
124
140
  if (!animation) return null;
@@ -315,6 +331,17 @@ function ServiceItemPB({
315
331
  return;
316
332
  }
317
333
 
334
+ if (isTrain) {
335
+ if (!selectedSeatKey) {
336
+ onShowSeatSelectionError?.(serviceItem.id);
337
+ return;
338
+ }
339
+ if (onTrainButtonClick) {
340
+ onTrainButtonClick();
341
+ return;
342
+ }
343
+ }
344
+
318
345
  onBookButtonPress();
319
346
  };
320
347
 
@@ -528,6 +555,10 @@ function ServiceItemPB({
528
555
  isPeru={isPeru}
529
556
  renderIcon={renderIcon}
530
557
  discountSeatPriceColor={colors.discountSeatPriceColor}
558
+ isTrain={isTrain}
559
+ selectedSeatKey={selectedSeatKey}
560
+ onSeatSelect={handleSeatSelect}
561
+ topLabelColor={colors.topLabelColor}
531
562
  />
532
563
  </div>
533
564
 
@@ -543,6 +574,18 @@ function ServiceItemPB({
543
574
  soldOutIcon={renderIcon("soldOutIcon", "14px")}
544
575
  onClick={checkMidnight}
545
576
  />
577
+ {showSeatSelectionError === serviceItem.id && isTrain && (
578
+ <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
579
+ <div
580
+ className="text-[9px] text-center whitespace-nowrap"
581
+ style={{
582
+ color: colors.seatPriceColor,
583
+ }}
584
+ >
585
+ Selecciona el tipo de servicio
586
+ </div>
587
+ </div>
588
+ )}
546
589
  {showLastSeats ? (
547
590
  <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
548
591
  {serviceItem?.available_seats < 10 &&
@@ -46,6 +46,7 @@ function ServiceItemMobile({
46
46
  isLinatal,
47
47
  viewersConfig,
48
48
  operatorLabel,
49
+ isTrain,
49
50
  }: MobileServiceItemProps): React.ReactElement {
50
51
  const isItemExpanded = serviceItem.id === isExpanded;
51
52
  const isPetSeat = (Object.keys(serviceItem?.pet_seat_info) || []).length > 0;
@@ -249,6 +250,7 @@ function ServiceItemMobile({
249
250
  serviceItem={serviceItem}
250
251
  showLastSeats={showLastSeats}
251
252
  discountSeatPriceColor={colors.discountSeatPriceColor}
253
+ isTrain={isTrain}
252
254
  />
253
255
 
254
256
  {hasDiscount && (
@@ -200,4 +200,5 @@ export interface MobileServiceItemProps {
200
200
  icon?: string; // optional icon URL
201
201
  };
202
202
  operatorLabel?: string;
203
+ isTrain?: boolean;
203
204
  }
@@ -226,6 +226,18 @@ export interface ServiceItemProps {
226
226
  showLoginModal?: any;
227
227
  isLoggedIn?: any;
228
228
  showLoginOption?: boolean;
229
+ isTrain?: boolean;
230
+ selectedSeatKey?: any;
231
+ onSeatSelect?: (
232
+ key: any,
233
+ price: number,
234
+ seatKey: string,
235
+ apiSeatType?: string,
236
+ ) => void;
237
+ onTrainButtonClick?: any;
238
+ showSeatSelectionError?: string | null;
239
+ onShowSeatSelectionError?: (serviceId: string) => void;
240
+ onClearSeatSelectionError?: () => void;
229
241
  isFlores?: boolean;
230
242
  operatorLabel?: string;
231
243
  }
@@ -7,6 +7,8 @@ interface SeatType {
7
7
  label: string;
8
8
  fare: number;
9
9
  key: any;
10
+ apiSeatType?: string;
11
+ api_seat_type?: string;
10
12
  }
11
13
 
12
14
  interface SeatSectionProps {
@@ -21,6 +23,15 @@ interface SeatSectionProps {
21
23
  serviceItem?: any;
22
24
  renderIcon?: (iconKey: string, size?: string) => React.ReactNode;
23
25
  discountSeatPriceColor?: string;
26
+ isTrain?: boolean;
27
+ selectedSeatKey?: any;
28
+ onSeatSelect?: (
29
+ key: any,
30
+ price: number,
31
+ seatKey: string,
32
+ apiSeatType?: string,
33
+ ) => void;
34
+ topLabelColor?: string;
24
35
  }
25
36
 
26
37
  function getAllSeatTypes(seatTypes: SeatType[]) {
@@ -31,6 +42,8 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
31
42
  let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
32
43
  label: val?.label,
33
44
  price: val?.fare,
45
+ key: val?.key,
46
+ apiSeatType: val?.apiSeatType || val?.api_seat_type,
34
47
  }));
35
48
 
36
49
  seatTypesWithPrices.sort((a, b) => a.price - b.price);
@@ -38,7 +51,7 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
38
51
  return seatTypesWithPrices;
39
52
  }
40
53
 
41
- function getSortedSeatTypes(seatTypes: SeatType[]) {
54
+ function getSortedSeatTypes(seatTypes: SeatType[], isTrain: any) {
42
55
  if (!seatTypes?.length) {
43
56
  return [{ label: "Salon cama", price: 0 }];
44
57
  }
@@ -52,7 +65,9 @@ function getSortedSeatTypes(seatTypes: SeatType[]) {
52
65
  seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
53
66
  }
54
67
 
55
- seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
68
+ if (!isTrain) {
69
+ seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
70
+ }
56
71
 
57
72
  const seenPrices = new Set<number>();
58
73
  seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
@@ -97,14 +112,18 @@ function SeatSection({
97
112
  priceColor,
98
113
  currencySign,
99
114
  removeDuplicateSeats,
115
+ selectedSeatKey,
116
+ onSeatSelect,
100
117
  isPeru,
101
118
  serviceItem,
102
119
  renderIcon,
103
120
  dpSeatColor,
104
121
  discountSeatPriceColor,
122
+ isTrain,
123
+ topLabelColor,
105
124
  }: SeatSectionProps): React.ReactElement {
106
125
  const uniqueSeats = getUniqueSeats(seatTypes);
107
- const sortedSeatTypes = getSortedSeatTypes(seatTypes);
126
+ const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
108
127
  const numberOfSeats = getNumberOfSeats(seatTypes);
109
128
  const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
110
129
 
@@ -116,22 +135,68 @@ function SeatSection({
116
135
  const renderSeatNames = () => {
117
136
  const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
118
137
 
119
- return seats.map((val, key: number) =>
120
- SEAT_EXCEPTIONS.includes(val.label) ? null : (
121
- <span
122
- key={key}
123
- className={`flex items-center justify-between text-[13.33px] ${
124
- isSoldOut ? "text-[#c0c0c0]" : ""
125
- }`}
138
+ return seats.map((val, key: number) => {
139
+ return SEAT_EXCEPTIONS.includes(val.label) ? null : (
140
+ <div
141
+ className="flex items-center"
142
+ style={isTrain ? { cursor: "pointer" } : undefined}
143
+ onClick={
144
+ isTrain && !isSoldOut
145
+ ? () =>
146
+ val.label === selectedSeatKey
147
+ ? onSeatSelect?.(null, 0, "", "")
148
+ : onSeatSelect?.(
149
+ val.label,
150
+ val.price,
151
+ val.key,
152
+ (val as any).apiSeatType,
153
+ )
154
+ : undefined
155
+ }
126
156
  >
127
- {typeof val.label === "string" || typeof val.label === "number"
128
- ? removeDuplicateSeats && isPeru
129
- ? CommonService.truncateSeatLabel(val.label)
130
- : val.label
131
- : null}
132
- </span>
133
- ),
134
- );
157
+ {isTrain && (
158
+ <div
159
+ style={{
160
+ border: `1px solid ${val.label === selectedSeatKey ? topLabelColor : "#ccc"}`,
161
+ borderRadius: "50%",
162
+ width: "14px",
163
+ height: "14px",
164
+ minWidth: "14px",
165
+ marginRight: "10px",
166
+ display: "flex",
167
+ alignItems: "center",
168
+ justifyContent: "center",
169
+ }}
170
+ >
171
+ {val.label === selectedSeatKey && (
172
+ <div
173
+ style={{
174
+ backgroundColor: topLabelColor,
175
+ borderRadius: "50%",
176
+ width: "7px",
177
+ height: "7px",
178
+ }}
179
+ />
180
+ )}
181
+ </div>
182
+ )}
183
+ <span
184
+ key={key}
185
+ className={`flex items-center justify-between text-[13.33px] ${
186
+ isSoldOut ? "text-[#c0c0c0]" : ""
187
+ }`}
188
+ >
189
+ {typeof val.label === "string" || typeof val.label === "number"
190
+ ? removeDuplicateSeats && isPeru
191
+ ? CommonService.truncateSeatLabel(val.label)
192
+ : isTrain
193
+ ? CommonService.truncateSeatLabel(val.label, 8)
194
+ : val.label
195
+ : null}
196
+ </span>
197
+ </div>
198
+ );
199
+ });
135
200
  };
136
201
 
137
202
  const renderSeatPrices = () => {
@@ -25,6 +25,7 @@ interface DateTimeSectionMobileProps {
25
25
  tooltipBgColor?: string;
26
26
  showLastSeats?: boolean;
27
27
  discountSeatPriceColor?: string;
28
+ isTrain?: boolean;
28
29
  }
29
30
 
30
31
  const pad = (n: number) => (n < 10 ? "0" + n : String(n));
@@ -67,41 +68,43 @@ const TimeRow: React.FC<TimeRowProps> = ({
67
68
  isSoldOut,
68
69
  }) => {
69
70
  const formattedDate = DateService.getServiceItemDate(date);
70
- const dotPositionClass = formattedDate.includes("dom") ? "max-[399px]:left-[53%]" : "";
71
- return <div
72
- className={`flex items-center min-[420]:text-[13px] text-[12px] justify-between ${
73
- isSoldOut ? "text-[#c0c0c0]" : ""
74
- }`}
75
- >
76
- <div className="flex items-center" style={{ flex: 1 }}>
77
- <div>
78
- {" "}
79
- {label ? (
80
- <div className="w-[60px]">{label}</div>
81
- ) : (
82
- <div className="w-[12px] h-auto mr-[5px]">
83
- <img
84
- src={icon}
85
- alt={alt}
86
- className={`w-[12px] h-auto mr-[5px] ${
87
- isSoldOut ? "grayscale" : ""
88
- }`}
89
- />
90
- </div>
91
- )}
92
- </div>
93
- <div
94
- className="flex items-center relative capitalize justify-between"
95
- style={{ flex: 1 }}
96
- >
97
- <span className="cursor-pointer black-text">
98
- {formattedDate}
99
- </span>
100
- <div className={`absolute left-[50%] ${dotPositionClass}`}>•</div>
101
- <div className="font-[900] relative black-text">{timeContent}</div>
71
+ const dotPositionClass = formattedDate.includes("dom")
72
+ ? "max-[399px]:left-[53%]"
73
+ : "";
74
+ return (
75
+ <div
76
+ className={`flex items-center min-[420]:text-[13px] text-[12px] justify-between ${
77
+ isSoldOut ? "text-[#c0c0c0]" : ""
78
+ }`}
79
+ >
80
+ <div className="flex items-center" style={{ flex: 1 }}>
81
+ <div>
82
+ {" "}
83
+ {label ? (
84
+ <div className="w-[60px]">{label}</div>
85
+ ) : (
86
+ <div className="w-[12px] h-auto mr-[5px]">
87
+ <img
88
+ src={icon}
89
+ alt={alt}
90
+ className={`w-[12px] h-auto mr-[5px] ${
91
+ isSoldOut ? "grayscale" : ""
92
+ }`}
93
+ />
94
+ </div>
95
+ )}
96
+ </div>
97
+ <div
98
+ className="flex items-center relative capitalize justify-between"
99
+ style={{ flex: 1 }}
100
+ >
101
+ <span className="cursor-pointer black-text">{formattedDate}</span>
102
+ <div className={`absolute left-[50%] ${dotPositionClass}`}>•</div>
103
+ <div className="font-[900] relative black-text">{timeContent}</div>
104
+ </div>
102
105
  </div>
103
106
  </div>
104
- </div>;
107
+ );
105
108
  };
106
109
 
107
110
  function DateTimeSectionMobile({
@@ -127,6 +130,7 @@ function DateTimeSectionMobile({
127
130
  tooltipBgColor,
128
131
  showLastSeats,
129
132
  discountSeatPriceColor,
133
+ isTrain,
130
134
  }: DateTimeSectionMobileProps): React.ReactElement {
131
135
  const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
132
136
 
@@ -198,6 +202,7 @@ function DateTimeSectionMobile({
198
202
  tooltipBgColor={tooltipBgColor}
199
203
  showLastSeats={showLastSeats}
200
204
  discountSeatPriceColor={discountSeatPriceColor}
205
+ isTrain={isTrain}
201
206
  />
202
207
  </div>
203
208
  );
@@ -31,6 +31,7 @@ interface SeatSectionMobileProps {
31
31
  tooltipBgColor?: string;
32
32
  showLastSeats?: boolean;
33
33
  discountSeatPriceColor?: string;
34
+ isTrain?: boolean;
34
35
  }
35
36
 
36
37
  interface SeatRowProps {
@@ -118,6 +119,7 @@ function SeatSectionMobile({
118
119
  tooltipBgColor,
119
120
  showLastSeats,
120
121
  discountSeatPriceColor,
122
+ isTrain,
121
123
  }: SeatSectionMobileProps): React.ReactElement {
122
124
  const hasMultipleTypes = (seatTypesData?.length ?? 0) > 2;
123
125
 
@@ -282,10 +284,11 @@ function SeatSectionMobile({
282
284
  ));
283
285
  }
284
286
 
285
- return seatTypesData
287
+ const filteredSeats = seatTypesData
286
288
  ?.filter((item) => getFilteredSeats(item.label))
287
- ?.sort((a, b) => a.fare - b.fare)
288
- ?.slice(0, 2)
289
+ ?.sort((a, b) => a.fare - b.fare);
290
+
291
+ return (isTrain ? filteredSeats : filteredSeats?.slice(0, 2))
289
292
  ?.map((type, i) => (
290
293
  <SeatRow
291
294
  key={i}
@@ -303,10 +306,12 @@ function SeatSectionMobile({
303
306
 
304
307
  const seats = removeDuplicateSeats
305
308
  ? getUniqueSeats(seatTypesData, 3)
306
- : seatTypesData
307
- ?.filter((item) => getFilteredSeats(item.label))
308
- ?.sort((a, b) => a.fare - b.fare)
309
- ?.slice(0, 2);
309
+ : (() => {
310
+ const filtered = seatTypesData
311
+ ?.filter((item) => getFilteredSeats(item.label))
312
+ ?.sort((a, b) => a.fare - b.fare);
313
+ return isTrain ? filtered : filtered?.slice(0, 2);
314
+ })();
310
315
 
311
316
  const discountedSeats = seats?.map((seat) => ({
312
317
  ...seat,
@@ -39,9 +39,15 @@ const commonService = {
39
39
  }
40
40
  },
41
41
 
42
- truncateSeatLabel: (label: string | number): string => {
42
+ truncateSeatLabel: (label: string | number, maxLength?: number): string => {
43
43
  if (typeof label !== "string") return String(label);
44
44
  if (label.includes("(")) return label;
45
+
46
+ // If maxLength provided, hard-truncate regardless of word count
47
+ if (maxLength != null && label.length > maxLength) {
48
+ return label.slice(0, maxLength) + "...";
49
+ }
50
+
45
51
  const words = label.trim().split(/\s+/);
46
52
 
47
53
  const truncateWord = (word: string) =>