kupos-ui-components-lib 9.9.8 → 9.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, isNewUi, showLoginModal, isLoggedIn, showLoginOption, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }: 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, isNewUi, showLoginModal, isLoggedIn, showLoginOption, isTrain, selectedSeatKey, onSeatSelect, onTrainButtonClick, showSeatSelectionError, onShowSeatSelectionError, onClearSeatSelectionError, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }: ServiceItemProps & {
4
4
  currencySign?: string;
5
5
  }): React.ReactElement;
6
6
  export default ServiceItemPB;
@@ -87,8 +87,12 @@ const ANIMATION_MAP = {
87
87
  kupos: flameAnimation,
88
88
  },
89
89
  };
90
- 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, isNewUi, showLoginModal, isLoggedIn, showLoginOption, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }) {
90
+ 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, isNewUi, showLoginModal, isLoggedIn, showLoginOption, isTrain, selectedSeatKey, onSeatSelect, onTrainButtonClick, showSeatSelectionError, onShowSeatSelectionError, onClearSeatSelectionError, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }) {
91
91
  var _a, _b, _c;
92
+ const handleSeatSelect = (key, price, seatKey, apiSeatType) => {
93
+ onClearSeatSelectionError === null || onClearSeatSelectionError === void 0 ? void 0 : onClearSeatSelectionError();
94
+ onSeatSelect === null || onSeatSelect === void 0 ? void 0 : onSeatSelect(key, price, seatKey, apiSeatType);
95
+ };
92
96
  const getAnimationIcon = (icon) => {
93
97
  var _a;
94
98
  const animation = ANIMATION_MAP[icon];
@@ -227,6 +231,16 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
227
231
  });
228
232
  return;
229
233
  }
234
+ if (isTrain) {
235
+ if (!selectedSeatKey) {
236
+ onShowSeatSelectionError === null || onShowSeatSelectionError === void 0 ? void 0 : onShowSeatSelectionError(serviceItem.id);
237
+ return;
238
+ }
239
+ if (onTrainButtonClick) {
240
+ onTrainButtonClick();
241
+ return;
242
+ }
243
+ }
230
244
  onBookButtonPress();
231
245
  };
232
246
  const items = [
@@ -293,9 +307,13 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
293
307
  backgroundColor: "#ccc",
294
308
  } }),
295
309
  React.createElement("div", { className: "content-center" },
296
- 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 })),
310
+ 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 })),
297
311
  React.createElement("div", { className: "relative" },
298
312
  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 }),
313
+ showSeatSelectionError === serviceItem.id && isTrain && (React.createElement("div", { className: "flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]" },
314
+ React.createElement("div", { className: "text-[9px] text-center whitespace-nowrap", style: {
315
+ color: colors.seatPriceColor,
316
+ } }, "Selecciona el tipo de servicio"))),
299
317
  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 &&
300
318
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) > 0 && (React.createElement("div", { className: "text-[12px] mt-1 text-center", style: {
301
319
  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, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, cityOrigin, cityDestination, isNewUi, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }: 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, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, cityOrigin, cityDestination, isNewUi, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }: MobileServiceItemProps): React.ReactElement;
4
4
  export default ServiceItemMobile;
@@ -18,7 +18,7 @@ const exceptions = [
18
18
  "blanco",
19
19
  "asiento_mascota",
20
20
  ];
21
- function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, cityOrigin, cityDestination, isNewUi, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }) {
21
+ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, orignLabel, destinationLabel, amenitiesData, setShowDropdown, showDropdown, isExpanded, setIsExpanded, setAmenetiesAtomValue, isCiva, currencySign, isPeru, showRating, showLastSeats, removeDuplicateSeats, isLinatal, viewersConfig, operatorLabel, isTrain, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, cityOrigin, cityDestination, isNewUi, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, }) {
22
22
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
23
23
  const isItemExpanded = serviceItem.id === isExpanded;
24
24
  const isPetSeat = (Object.keys(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.pet_seat_info) || []).length > 0;
@@ -99,7 +99,9 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
99
99
  showTopLabel
100
100
  ? "mt-[20px]"
101
101
  : "mt-[10px]"} `, style: { zIndex: 1 } },
102
- React.createElement("div", { className: `z-1 ${((hasOfferText && isNewUiEnabled) || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled)) && !isSoldOut
102
+ React.createElement("div", { className: `z-1 ${((hasOfferText && isNewUiEnabled) ||
103
+ (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled)) &&
104
+ !isSoldOut
103
105
  ? "rounded-[18px]"
104
106
  : "rounded-[10px] border border-[#ccc]"}`, style: Object.assign(Object.assign({}, serviceCardStyle), { backgroundColor: "#fff" }) },
105
107
  React.createElement("div", { style: { padding: "12px 12px 8px 12px" } },
@@ -129,8 +131,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
129
131
  setIsExpanded(isItemExpanded ? null : serviceItem.id);
130
132
  }, isPeru: isPeru, femaleAnim: serviceItem.icons.femaleAnim, ladiesBookedSeats: serviceItem.ladies_booked_seats, isDpEnabled: serviceItem.is_dp_enabled })),
131
133
  React.createElement(ServiceBadgesMobile, { showTopLabel: showTopLabel, isSoldOut: isSoldOut, colors: colors, renderIcon: renderIcon, serviceItem: serviceItem, isConexion: isConexion })),
132
- (((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && !isNewUiEnabled)) &&
133
- !isSoldOut && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]", style: {
134
+ (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && !isNewUiEnabled && !isSoldOut && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]", style: {
134
135
  background: colors === null || colors === void 0 ? void 0 : colors.bottomStripColor,
135
136
  borderRadius: "0 0 14px 14px",
136
137
  zIndex: -1,
@@ -145,7 +146,8 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
145
146
  lineHeight: 1.4,
146
147
  } },
147
148
  React.createElement("span", { className: "whitespace-nowrap min-[380px]:text-[12px]" }, serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text))))))),
148
- (((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && isNewUiEnabled) || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled)) &&
149
+ (((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && isNewUiEnabled) ||
150
+ (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled)) &&
149
151
  !isSoldOut && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]", style: {
150
152
  background: isSoldOut
151
153
  ? offerGradientWithOpacity
@@ -215,6 +215,8 @@ export interface MobileServiceItemProps {
215
215
  label?: string;
216
216
  icon?: string;
217
217
  };
218
+ operatorLabel?: string;
219
+ isTrain?: boolean;
218
220
  isFeatureDropDownExpand?: any;
219
221
  setIsFeatureDropDownExpand?: (value: any) => void;
220
222
  ticketQuantity?: number;
@@ -245,6 +245,13 @@ export interface ServiceItemProps {
245
245
  showLoginModal?: any;
246
246
  isLoggedIn?: any;
247
247
  showLoginOption?: boolean;
248
+ isTrain?: boolean;
249
+ selectedSeatKey?: any;
250
+ onSeatSelect?: (key: any, price: number, seatKey: string, apiSeatType?: string) => void;
251
+ onTrainButtonClick?: any;
252
+ showSeatSelectionError?: string | null;
253
+ onShowSeatSelectionError?: (serviceId: string) => void;
254
+ onClearSeatSelectionError?: () => void;
248
255
  selectedTimeSlot?: string;
249
256
  onTimeSlotChange?: (slot: string) => void;
250
257
  isTimeDropdownOpen?: string | number | null;
package/dist/styles.css CHANGED
@@ -996,6 +996,9 @@
996
996
  .text-right {
997
997
  text-align: right;
998
998
  }
999
+ .text-\[9px\] {
1000
+ font-size: 9px;
1001
+ }
999
1002
  .text-\[10px\] {
1000
1003
  font-size: 10px;
1001
1004
  }
@@ -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,
@@ -47,7 +49,11 @@ function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal
47
49
  ? null
48
50
  : DateService.ampmOnly(depTime)))) : (DateService.formatTime(depTime));
49
51
  return (React.createElement("div", { className: "flex justify-between gap-[5px] w-full", onClick: onBookButtonPress },
50
- React.createElement("div", { className: "min-h-[2.5rem] flex flex-col justify-between gap-[4px] w-[50%] ", style: { justifyContent: isCiva && "center" } },
52
+ React.createElement("div", { className: `flex flex-col gap-[4px] w-[50%] ${isTrain ? "justify-center" : "justify-between"}`, style: {
53
+ justifyContent: isCiva && "center",
54
+ minHeight: isTrain ? undefined : "2.5rem",
55
+ alignSelf: isTrain ? "stretch" : undefined,
56
+ } },
51
57
  React.createElement(TimeRow, { label: orignLabel, icon: originIcon, alt: "origin", date: travelDate, timeContent: depTimeContent, isSoldOut: isSoldOut }),
52
58
  isCiva ? null : (React.createElement(TimeRow, { label: destinationLabel, icon: destinationIcon, alt: "destination", date: arrivalDate, timeContent: DateService.formatTime(arrTime), isSoldOut: isSoldOut }))),
53
59
  React.createElement("div", { style: {
@@ -56,6 +62,6 @@ function DateTimeSectionMobile({ onBookButtonPress, isCiva, isSoldOut, isLinatal
56
62
  backgroundColor: "#ccc",
57
63
  margin: "auto",
58
64
  } }),
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 })));
65
+ 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
66
  }
61
67
  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;
@@ -11,7 +11,7 @@ const EXCEPTIONS = [
11
11
  "blanco",
12
12
  "asiento_mascota",
13
13
  ];
14
- const SeatRow = ({ type, index, displayLabel, fare, isSoldOut, seatPriceColor, hasMultipleTypes, textSize, }) => {
14
+ const SeatRow = ({ type, index, displayLabel, fare, isSoldOut, seatPriceColor, hasMultipleTypes, textSize, isTrain, }) => {
15
15
  if (EXCEPTIONS.includes(type.label))
16
16
  return null;
17
17
  const rowClass = hasMultipleTypes
@@ -20,7 +20,9 @@ const SeatRow = ({ type, index, displayLabel, fare, isSoldOut, seatPriceColor, h
20
20
  const labelColor = isSoldOut ? "#bbb" : "#464647";
21
21
  const priceColor = isSoldOut ? "#bbb" : seatPriceColor;
22
22
  return (React.createElement("div", { className: rowClass, key: index },
23
- React.createElement("span", { className: `min-[420]:text-[13px] ${textSize} `, style: { color: labelColor } }, displayLabel),
23
+ React.createElement("span", { className: `min-[420]:text-[13px] ${textSize} `, style: { color: labelColor } }, isTrain
24
+ ? commonService.truncateSeatLabel(displayLabel, 8)
25
+ : displayLabel),
24
26
  React.createElement("span", { className: `min-[420]:text-[13px] ${textSize} bold-text`, style: { color: priceColor } }, fare)));
25
27
  };
26
28
  const getFilteredSeats = (item) => {
@@ -42,8 +44,8 @@ const getUniqueSeats = (data, limit) => {
42
44
  .sort((a, b) => a.fare - b.fare)
43
45
  .slice(0, limit);
44
46
  };
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;
47
+ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, isTrain, }) {
48
+ var _a, _b, _c, _d, _e, _f, _g;
47
49
  const hasMultipleTypes = ((_a = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.length) !== null && _a !== void 0 ? _a : 0) > 2;
48
50
  const getFare = (fare) => {
49
51
  if (removeDuplicateSeats && availableSeats <= 0 && !isPeru) {
@@ -101,19 +103,24 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
101
103
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] text-[#464647]", style: { opacity: isSoldOut ? 0.5 : 1 } }, "Agotado"))) : null));
102
104
  };
103
105
  const renderSeats = () => {
104
- var _a, _b, _c;
106
+ var _a, _b;
105
107
  if (isPeru) {
106
108
  return renderPeruSeats();
107
109
  }
108
110
  if (removeDuplicateSeats) {
109
111
  const uniqueSeats = getUniqueSeats(seatTypesData, 3);
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]" })));
112
+ 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]", isTrain: isTrain })));
111
113
  }
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]" })));
114
+ 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);
115
+ 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]", isTrain: isTrain })));
113
116
  };
114
117
  const seats = removeDuplicateSeats
115
118
  ? 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);
119
+ : (() => {
120
+ var _a;
121
+ 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);
122
+ return isTrain ? filtered : filtered === null || filtered === void 0 ? void 0 : filtered.slice(0, 2);
123
+ })();
117
124
  const discountedSeats = seats === null || seats === void 0 ? void 0 : seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
118
125
  const peruLowestFare = isPeru ? getLowestFare() : null;
119
126
  const peruDiscountCalc = isPeru && peruLowestFare != null
@@ -124,7 +131,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
124
131
  peruDiscountCalc.originalPrice !== peruDiscountCalc.discountedPrice
125
132
  : discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
126
133
  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];
134
+ ? 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
135
  const discountValue = (() => {
129
136
  if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_type) === "percentage" &&
130
137
  typeof (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) === "number") {
@@ -147,14 +154,14 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
147
154
  };
148
155
  const originalDpPrice = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.original_dp_price);
149
156
  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;
157
+ 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
158
  const hasDpDiscount = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) &&
152
159
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) &&
153
160
  originalDpPrice != null &&
154
161
  dpDiscountPercent != null &&
155
162
  firstSeatFare != null;
156
163
  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) &&
164
+ !((_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _e === void 0 ? void 0 : _e.length) &&
158
165
  !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
166
  !isNaN(Number(dpDiscountPercent)) &&
160
167
  Number(dpDiscountPercent) > 0 && (React.createElement("div", { className: "absolute -top-[18px] right-[0px]", style: {
@@ -186,7 +193,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
186
193
  React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
187
194
  color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
188
195
  } },
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,
196
+ ((_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
197
  commonService.discountedCurrency(Number(firstSeatFare), currencySign)),
191
198
  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
199
  React.createElement("div", { className: "relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] " },
@@ -220,8 +227,8 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
220
227
  React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
221
228
  color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
222
229
  } },
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,
224
- commonService.discountedCurrency(discountSeat.discountedPrice, currencySign))))) : (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem] ", style: {
230
+ ((_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,
231
+ commonService.discountedCurrency(discountSeat.discountedPrice, currencySign))))) : (React.createElement("div", { className: `flex flex-col justify-between ${isTrain ? "" : "h-[2.5rem]"} `, style: {
225
232
  gap: isSoldOut ? "0px" : "5px",
226
233
  justifyContent: hasMultipleTypes ? "space-between" : "center",
227
234
  } },
@@ -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.8",
3
+ "version": "9.9.9",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -131,6 +131,13 @@ function ServiceItemPB({
131
131
  showLoginModal,
132
132
  isLoggedIn,
133
133
  showLoginOption,
134
+ isTrain,
135
+ selectedSeatKey,
136
+ onSeatSelect,
137
+ onTrainButtonClick,
138
+ showSeatSelectionError,
139
+ onShowSeatSelectionError,
140
+ onClearSeatSelectionError,
134
141
  isFeatureDropDownExpand,
135
142
  setIsFeatureDropDownExpand,
136
143
  ticketQuantity,
@@ -144,6 +151,15 @@ function ServiceItemPB({
144
151
  wowDealData,
145
152
  isFlores,
146
153
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
154
+ const handleSeatSelect = (
155
+ key: any,
156
+ price: number,
157
+ seatKey: string,
158
+ apiSeatType?: string,
159
+ ) => {
160
+ onClearSeatSelectionError?.();
161
+ onSeatSelect?.(key, price, seatKey, apiSeatType);
162
+ };
147
163
  const getAnimationIcon = (icon: string) => {
148
164
  const animation = ANIMATION_MAP[icon];
149
165
  if (!animation) return null;
@@ -341,6 +357,17 @@ function ServiceItemPB({
341
357
  return;
342
358
  }
343
359
 
360
+ if (isTrain) {
361
+ if (!selectedSeatKey) {
362
+ onShowSeatSelectionError?.(serviceItem.id);
363
+ return;
364
+ }
365
+ if (onTrainButtonClick) {
366
+ onTrainButtonClick();
367
+ return;
368
+ }
369
+ }
370
+
344
371
  onBookButtonPress();
345
372
  };
346
373
 
@@ -594,6 +621,10 @@ function ServiceItemPB({
594
621
  isPeru={isPeru}
595
622
  renderIcon={renderIcon}
596
623
  discountSeatPriceColor={colors.discountSeatPriceColor}
624
+ isTrain={isTrain}
625
+ selectedSeatKey={selectedSeatKey}
626
+ onSeatSelect={handleSeatSelect}
627
+ topLabelColor={colors.topLabelColor}
597
628
  />
598
629
  </div>
599
630
 
@@ -609,6 +640,18 @@ function ServiceItemPB({
609
640
  soldOutIcon={renderIcon("soldOutIcon", "14px")}
610
641
  onClick={checkMidnight}
611
642
  />
643
+ {showSeatSelectionError === serviceItem.id && isTrain && (
644
+ <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
645
+ <div
646
+ className="text-[9px] text-center whitespace-nowrap"
647
+ style={{
648
+ color: colors.seatPriceColor,
649
+ }}
650
+ >
651
+ Selecciona el tipo de servicio
652
+ </div>
653
+ </div>
654
+ )}
612
655
  {showLastSeats ? (
613
656
  <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
614
657
  {serviceItem?.available_seats < 10 &&
@@ -46,6 +46,8 @@ function ServiceItemMobile({
46
46
  removeDuplicateSeats,
47
47
  isLinatal,
48
48
  viewersConfig,
49
+ operatorLabel,
50
+ isTrain,
49
51
  isFeatureDropDownExpand,
50
52
  setIsFeatureDropDownExpand,
51
53
  ticketQuantity,
@@ -215,7 +217,9 @@ function ServiceItemMobile({
215
217
  >
216
218
  <div
217
219
  className={`z-1 ${
218
- ((hasOfferText && isNewUiEnabled) || serviceItem?.is_dp_enabled) && !isSoldOut
220
+ ((hasOfferText && isNewUiEnabled) ||
221
+ serviceItem?.is_dp_enabled) &&
222
+ !isSoldOut
219
223
  ? "rounded-[18px]"
220
224
  : "rounded-[10px] border border-[#ccc]"
221
225
  }`}
@@ -231,7 +235,9 @@ function ServiceItemMobile({
231
235
  serviceItem?.offer_text || hasDiscount ? "10px" : "",
232
236
  }}
233
237
  >
234
- <div className={`w-[115px] ${isFlores ? "" : "overflow-y-hidden"} h-[22px]`}>
238
+ <div
239
+ className={`w-[115px] ${isFlores ? "" : "overflow-y-hidden"} h-[22px]`}
240
+ >
235
241
  <img
236
242
  src={serviceItem.operator_details[0]}
237
243
  alt="service logo"
@@ -371,50 +377,50 @@ function ServiceItemMobile({
371
377
  </div>
372
378
 
373
379
  {/* 🔹 EXPANDABLE DROPDOWN (below the card) */}
374
- {((serviceItem?.offer_text && !isNewUiEnabled)) &&
375
- !isSoldOut && (
380
+ {serviceItem?.offer_text && !isNewUiEnabled && !isSoldOut && (
381
+ <div
382
+ className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
383
+ style={{
384
+ background: colors?.bottomStripColor,
385
+ borderRadius: "0 0 14px 14px",
386
+ zIndex: -1,
387
+ }}
388
+ >
376
389
  <div
377
- className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
378
- style={{
379
- background: colors?.bottomStripColor,
380
- borderRadius: "0 0 14px 14px",
381
- zIndex: -1,
382
- }}
390
+ className="flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]"
391
+ style={{ lineHeight: 1.6 }}
383
392
  >
384
- <div
385
- className="flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]"
386
- style={{ lineHeight: 1.6 }}
387
- >
388
- <div className="flex justify-between items-center">
389
- <div
390
- className={`flex ${(serviceItem?.offer_text || "").length > 10 ? "items-start" : "items-center"}`}
391
- >
392
- <div className={isLongOfferText ? "mt-[2px]" : ""}>
393
- <LottiePlayer
394
- animationData={serviceItem.icons.promoAnim}
395
- width="14px"
396
- height="14px"
397
- />
398
- </div>
399
- <div
400
- className={`ml-[4px] flex-1 outline-none ${isLongOfferText ? "mt-[2px]" : ""}`}
401
- style={{
402
- color: "#fff",
403
- lineHeight: 1.4,
404
- }}
405
- >
406
- <span className="whitespace-nowrap min-[380px]:text-[12px]">
407
- {serviceItem?.offer_text}
408
- </span>
409
- </div>
410
- </div>
393
+ <div className="flex justify-between items-center">
394
+ <div
395
+ className={`flex ${(serviceItem?.offer_text || "").length > 10 ? "items-start" : "items-center"}`}
396
+ >
397
+ <div className={isLongOfferText ? "mt-[2px]" : ""}>
398
+ <LottiePlayer
399
+ animationData={serviceItem.icons.promoAnim}
400
+ width="14px"
401
+ height="14px"
402
+ />
403
+ </div>
404
+ <div
405
+ className={`ml-[4px] flex-1 outline-none ${isLongOfferText ? "mt-[2px]" : ""}`}
406
+ style={{
407
+ color: "#fff",
408
+ lineHeight: 1.4,
409
+ }}
410
+ >
411
+ <span className="whitespace-nowrap min-[380px]:text-[12px]">
412
+ {serviceItem?.offer_text}
413
+ </span>
414
+ </div>
411
415
  </div>
412
416
  </div>
413
417
  </div>
414
- )}
418
+ </div>
419
+ )}
415
420
 
416
421
  {/* 🔹 EXPANDABLE DROPDOWN (below the card) */}
417
- {((serviceItem?.offer_text && isNewUiEnabled) || serviceItem?.is_dp_enabled) &&
422
+ {((serviceItem?.offer_text && isNewUiEnabled) ||
423
+ serviceItem?.is_dp_enabled) &&
418
424
  !isSoldOut && (
419
425
  <div
420
426
  className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
@@ -135,36 +135,36 @@ export interface MobileServiceItemProps {
135
135
  bombAnim?: string;
136
136
  whiteBoardingIcon?: string;
137
137
  downArrow?: string;
138
- personIcon?: string
138
+ personIcon?: string;
139
139
  specialDeparture?: string;
140
140
  fireIcon?: string;
141
141
  directoIcon?: string;
142
- whiteFireIcon?: string
143
- femaleAnim?:string
144
- thunderAnim?: string
145
- personsAnim?: string
146
- whiteOrigin?: string,
147
- whiteDestination?: string,
148
- userIcon?: string,
149
-
150
- sheildIcon?: string,
151
- busIcon?: string,
152
- whiteDownArrow?: string,
153
- empressaIcon?: string,
154
- flexibleIcon?: string,
155
- listoIcon?: string,
156
- precioIcon?: string,
157
- confirmarIcon?: string
142
+ whiteFireIcon?: string;
143
+ femaleAnim?: string;
144
+ thunderAnim?: string;
145
+ personsAnim?: string;
146
+ whiteOrigin?: string;
147
+ whiteDestination?: string;
148
+ userIcon?: string;
149
+
150
+ sheildIcon?: string;
151
+ busIcon?: string;
152
+ whiteDownArrow?: string;
153
+ empressaIcon?: string;
154
+ flexibleIcon?: string;
155
+ listoIcon?: string;
156
+ precioIcon?: string;
157
+ confirmarIcon?: string;
158
158
  cancelTicketIcon?: string;
159
159
  changeTicketIcon?: string;
160
160
  petFriendlyIcon?: string;
161
- womenSeatIcon?: string
161
+ womenSeatIcon?: string;
162
162
  [key: string]: string | Record<string, string | undefined> | undefined;
163
163
  };
164
164
  useLottieFor?: string[];
165
165
  };
166
166
  onBookButtonPress?: () => void;
167
- onRemateUiButtonClick?: ()=> void;
167
+ onRemateUiButtonClick?: () => void;
168
168
  terminals?: any[];
169
169
  showDropdown?: boolean;
170
170
  setShowDropdown?: (value: boolean) => void;
@@ -208,7 +208,7 @@ export interface MobileServiceItemProps {
208
208
  seatPriceColor?: string;
209
209
  rightGradiantColor?: string;
210
210
  leftGradiantColor?: string;
211
- discountSeatPriceColor?: string
211
+ discountSeatPriceColor?: string;
212
212
  };
213
213
  isCiva?: boolean;
214
214
  currencySign?: string;
@@ -221,23 +221,29 @@ export interface MobileServiceItemProps {
221
221
  showLastSeats?: boolean;
222
222
  removeDuplicateSeats?: boolean;
223
223
  isLinatal?: boolean;
224
- viewersConfig?: {
224
+ viewersConfig?: {
225
225
  min: number;
226
226
  max: number;
227
227
  interval?: number; // ms, default 5000
228
228
  label?: string; // e.g. "personas están viendo este viaje"
229
229
  icon?: string; // optional icon URL
230
230
  };
231
- isFeatureDropDownExpand?: any;
231
+ operatorLabel?: string;
232
+ isTrain?: boolean;
233
+ isFeatureDropDownExpand?: any;
232
234
  setIsFeatureDropDownExpand?: (value: any) => void;
233
235
  ticketQuantity?: number;
234
- onIncreaseTicketQuantity?: (serviceItem: MobileServiceItemProps["serviceItem"]) => void;
235
- onDecreaseTicketQuantity?: (serviceItem: MobileServiceItemProps["serviceItem"]) => void;
236
+ onIncreaseTicketQuantity?: (
237
+ serviceItem: MobileServiceItemProps["serviceItem"],
238
+ ) => void;
239
+ onDecreaseTicketQuantity?: (
240
+ serviceItem: MobileServiceItemProps["serviceItem"],
241
+ ) => void;
236
242
  cityOrigin?: { value: number; label: string };
237
243
  cityDestination?: { value: number; label: string };
238
- isNewUi?: boolean
244
+ isNewUi?: boolean;
239
245
 
240
- selectedTimeSlot?: string;
246
+ selectedTimeSlot?: string;
241
247
  onTimeSlotChange?: (slot: string) => void;
242
248
  isTimeDropdownOpen?: string | number | null;
243
249
  onTimeDropdownToggle?: (id?: string | number | null) => void;
@@ -252,6 +252,18 @@ export interface ServiceItemProps {
252
252
  showLoginModal?: any;
253
253
  isLoggedIn?: any;
254
254
  showLoginOption?: boolean;
255
+ isTrain?: boolean;
256
+ selectedSeatKey?: any;
257
+ onSeatSelect?: (
258
+ key: any,
259
+ price: number,
260
+ seatKey: string,
261
+ apiSeatType?: string,
262
+ ) => void;
263
+ onTrainButtonClick?: any;
264
+ showSeatSelectionError?: string | null;
265
+ onShowSeatSelectionError?: (serviceId: string) => void;
266
+ onClearSeatSelectionError?: () => void;
255
267
  selectedTimeSlot?: string;
256
268
  onTimeSlotChange?: (slot: string) => void;
257
269
  isTimeDropdownOpen?: string | number | null;
@@ -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
 
@@ -153,8 +157,12 @@ function DateTimeSectionMobile({
153
157
  >
154
158
  {/* DATE AND TIME */}
155
159
  <div
156
- className="min-h-[2.5rem] flex flex-col justify-between gap-[4px] w-[50%] "
157
- style={{ justifyContent: isCiva && "center" }}
160
+ className={`flex flex-col gap-[4px] w-[50%] ${isTrain ? "justify-center" : "justify-between"}`}
161
+ style={{
162
+ justifyContent: isCiva && "center",
163
+ minHeight: isTrain ? undefined : "2.5rem",
164
+ alignSelf: isTrain ? "stretch" : undefined,
165
+ }}
158
166
  >
159
167
  <TimeRow
160
168
  label={orignLabel}
@@ -198,6 +206,7 @@ function DateTimeSectionMobile({
198
206
  tooltipBgColor={tooltipBgColor}
199
207
  showLastSeats={showLastSeats}
200
208
  discountSeatPriceColor={discountSeatPriceColor}
209
+ isTrain={isTrain}
201
210
  />
202
211
  </div>
203
212
  );
@@ -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 {
@@ -42,6 +43,7 @@ interface SeatRowProps {
42
43
  seatPriceColor: string;
43
44
  hasMultipleTypes: boolean;
44
45
  textSize: string;
46
+ isTrain?: boolean;
45
47
  }
46
48
 
47
49
  const SeatRow: React.FC<SeatRowProps> = ({
@@ -53,6 +55,7 @@ const SeatRow: React.FC<SeatRowProps> = ({
53
55
  seatPriceColor,
54
56
  hasMultipleTypes,
55
57
  textSize,
58
+ isTrain,
56
59
  }) => {
57
60
  if (EXCEPTIONS.includes(type.label)) return null;
58
61
 
@@ -69,7 +72,9 @@ const SeatRow: React.FC<SeatRowProps> = ({
69
72
  className={`min-[420]:text-[13px] ${textSize} `}
70
73
  style={{ color: labelColor }}
71
74
  >
72
- {displayLabel}
75
+ {isTrain
76
+ ? commonService.truncateSeatLabel(displayLabel, 8)
77
+ : displayLabel}
73
78
  </span>
74
79
  <span
75
80
  className={`min-[420]:text-[13px] ${textSize} bold-text`}
@@ -118,6 +123,7 @@ function SeatSectionMobile({
118
123
  tooltipBgColor,
119
124
  showLastSeats,
120
125
  discountSeatPriceColor,
126
+ isTrain,
121
127
  }: SeatSectionMobileProps): React.ReactElement {
122
128
  const hasMultipleTypes = (seatTypesData?.length ?? 0) > 2;
123
129
 
@@ -278,15 +284,17 @@ function SeatSectionMobile({
278
284
  seatPriceColor={seatPriceColor}
279
285
  hasMultipleTypes={hasMultipleTypes}
280
286
  textSize="text-[11px]"
287
+ isTrain={isTrain}
281
288
  />
282
289
  ));
283
290
  }
284
291
 
285
- return seatTypesData
292
+ const filteredSeats = seatTypesData
286
293
  ?.filter((item) => getFilteredSeats(item.label))
287
- ?.sort((a, b) => a.fare - b.fare)
288
- ?.slice(0, 2)
289
- ?.map((type, i) => (
294
+ ?.sort((a, b) => a.fare - b.fare);
295
+
296
+ return (isTrain ? filteredSeats : filteredSeats?.slice(0, 2))?.map(
297
+ (type, i) => (
290
298
  <SeatRow
291
299
  key={i}
292
300
  type={type}
@@ -297,16 +305,20 @@ function SeatSectionMobile({
297
305
  seatPriceColor={seatPriceColor}
298
306
  hasMultipleTypes={hasMultipleTypes}
299
307
  textSize="text-[12px]"
308
+ isTrain={isTrain}
300
309
  />
301
- ));
310
+ ),
311
+ );
302
312
  };
303
313
 
304
314
  const seats = removeDuplicateSeats
305
315
  ? getUniqueSeats(seatTypesData, 3)
306
- : seatTypesData
307
- ?.filter((item) => getFilteredSeats(item.label))
308
- ?.sort((a, b) => a.fare - b.fare)
309
- ?.slice(0, 2);
316
+ : (() => {
317
+ const filtered = seatTypesData
318
+ ?.filter((item) => getFilteredSeats(item.label))
319
+ ?.sort((a, b) => a.fare - b.fare);
320
+ return isTrain ? filtered : filtered?.slice(0, 2);
321
+ })();
310
322
 
311
323
  const discountedSeats = seats?.map((seat) => ({
312
324
  ...seat,
@@ -541,7 +553,7 @@ function SeatSectionMobile({
541
553
  </div>
542
554
  ) : (
543
555
  <div
544
- className="flex flex-col justify-between h-[2.5rem] "
556
+ className={`flex flex-col justify-between ${isTrain ? "" : "h-[2.5rem]"} `}
545
557
  style={{
546
558
  gap: isSoldOut ? "0px" : "5px",
547
559
  justifyContent: hasMultipleTypes ? "space-between" : "center",
@@ -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) =>