kupos-ui-components-lib 9.11.2 → 9.11.3

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, 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, isNewUi, showLoginModal, isLoggedIn, showLoginOption, isTrain, selectedSeatKey, onSeatSelect, onTrainButtonClick, showSeatSelectionError, onShowSeatSelectionError, onClearSeatSelectionError, isFeatureDropDownExpand, setIsFeatureDropDownExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onRemateUiButtonClick, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, isFlores, operatorLabel, }: ServiceItemProps & {
4
4
  currencySign?: string;
5
5
  }): React.ReactElement;
6
6
  export default ServiceItemPB;
@@ -87,9 +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, operatorLabel, }) {
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, operatorLabel, }) {
91
91
  var _a, _b, _c;
92
- console.log("🚀 ~ ServiceItemPB ~ serviceItem:", serviceItem);
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
+ };
93
96
  const getAnimationIcon = (icon) => {
94
97
  var _a;
95
98
  const animation = ANIMATION_MAP[icon];
@@ -246,6 +249,16 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
246
249
  });
247
250
  return;
248
251
  }
252
+ if (isTrain) {
253
+ if (!selectedSeatKey) {
254
+ onShowSeatSelectionError === null || onShowSeatSelectionError === void 0 ? void 0 : onShowSeatSelectionError(serviceItem.id);
255
+ return;
256
+ }
257
+ if (onTrainButtonClick) {
258
+ onTrainButtonClick();
259
+ return;
260
+ }
261
+ }
249
262
  onBookButtonPress();
250
263
  };
251
264
  const items = [
@@ -300,7 +313,7 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
300
313
  ? "14px"
301
314
  : "",
302
315
  } },
303
- React.createElement("div", { className: "grid text-[#464647] w-full [grid-template-columns:20%_30%_2.5%_24%_15.5%] gap-x-[2%] items-center" },
316
+ React.createElement("div", { className: `grid text-[#464647] w-full ${isTrain ? "[grid-template-columns:16%_30%_2.5%_28%_15.5%]" : "[grid-template-columns:20%_30%_2.5%_24%_15.5%]"} gap-x-[2%] items-center` },
304
317
  React.createElement("div", { className: "flex flex-col gap-[5px]" },
305
318
  React.createElement("div", null,
306
319
  React.createElement("img", { src: serviceItem.operator_details[0], alt: "service logo", className: `h-[30px] w-[auto] ${isSoldOut ? "grayscale" : ""}` }),
@@ -313,9 +326,13 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
313
326
  backgroundColor: "#ccc",
314
327
  } }),
315
328
  React.createElement("div", { className: "content-center" },
316
- 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, tooltipColor: colors.tooltipColor })),
329
+ 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, tooltipColor: colors.tooltipColor })),
317
330
  React.createElement("div", { className: "relative" },
318
331
  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 }),
332
+ showSeatSelectionError === serviceItem.id && isTrain && (React.createElement("div", { className: "flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]" },
333
+ React.createElement("div", { className: "text-[9px] text-center whitespace-nowrap", style: {
334
+ color: colors.seatPriceColor,
335
+ } }, "Selecciona el tipo de servicio"))),
319
336
  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 &&
320
337
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) > 0 && (React.createElement("div", { className: "text-[12px] mt-1 text-center", style: {
321
338
  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, 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, 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, operatorLabel }) {
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;
@@ -215,6 +215,7 @@ export interface MobileServiceItemProps {
215
215
  label?: string;
216
216
  icon?: string;
217
217
  };
218
+ isTrain?: boolean;
218
219
  isFeatureDropDownExpand?: any;
219
220
  setIsFeatureDropDownExpand?: (value: any) => void;
220
221
  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
@@ -566,6 +566,9 @@
566
566
  .\[grid-template-columns\:14\%_40\%_0\.5\%_24\%_13\.5\%\] {
567
567
  grid-template-columns: 14% 40% 0.5% 24% 13.5%;
568
568
  }
569
+ .\[grid-template-columns\:16\%_30\%_2\.5\%_28\%_15\.5\%\] {
570
+ grid-template-columns: 16% 30% 2.5% 28% 15.5%;
571
+ }
569
572
  .\[grid-template-columns\:20\%_30\%_2\.5\%_24\%_15\.5\%\] {
570
573
  grid-template-columns: 20% 30% 2.5% 24% 15.5%;
571
574
  }
@@ -1023,6 +1026,9 @@
1023
1026
  .text-right {
1024
1027
  text-align: right;
1025
1028
  }
1029
+ .text-\[9px\] {
1030
+ font-size: 9px;
1031
+ }
1026
1032
  .text-\[10px\] {
1027
1033
  font-size: 10px;
1028
1034
  }
@@ -32,16 +32,16 @@ function DateTimeSection({ serviceItem, isSoldOut, isCiva, isLinatal, removeArri
32
32
  React.createElement("img", { src: (_b = serviceItem.icons) === null || _b === void 0 ? void 0 : _b.destination, className: `w-[16px] h-auto mr-[8px] ${isSoldOut ? "grayscale" : ""}`, style: { opacity: isSoldOut ? 0.5 : 1 } }))))),
33
33
  React.createElement("div", { className: "flex flex-col gap-[4px]" },
34
34
  React.createElement(StageTooltip, { stageData: serviceItem.boarding_stages, direction: 1, terminals: busStage, serviceItem: serviceItem, metaData: metaData, colors: colors },
35
- React.createElement("span", { className: "cursor-pointer bold-text capitalize" }, DateService.getServiceItemDate(serviceItem.travel_date))),
35
+ React.createElement("span", { className: "cursor-pointer bold-text capitalize whitespace-nowrap" }, DateService.getServiceItemDate(serviceItem.travel_date))),
36
36
  !isCiva && (React.createElement(StageTooltip, { stageData: serviceItem.dropoff_stages, direction: 2, terminals: busStage, serviceItem: serviceItem, metaData: metaData, colors: colors },
37
- React.createElement("span", { className: "cursor-pointer bold-text capitalize" }, DateService.getServiceItemDate(serviceItem.arrival_date))))),
37
+ React.createElement("span", { className: "cursor-pointer bold-text capitalize whitespace-nowrap" }, DateService.getServiceItemDate(serviceItem.arrival_date))))),
38
38
  React.createElement("div", { className: "flex flex-col gap-[4px] items-center" },
39
39
  React.createElement("div", { className: "h-[20px] flex items-center justify-center" },
40
40
  React.createElement("div", null, "\u2022")),
41
41
  !isCiva && (React.createElement("div", { className: "h-[20px] flex items-center justify-center" }, removeArrivalTime ? null : serviceItem.arr_time ? (React.createElement("div", null, "\u2022")) : null))),
42
42
  React.createElement("div", { className: "flex flex-col gap-[4px]" },
43
43
  React.createElement(StageTooltip, { stageData: serviceItem.boarding_stages, direction: 1, terminals: busStage, serviceItem: serviceItem, metaData: metaData, colors: colors },
44
- React.createElement("div", { className: "font-[900] bold-text" }, isLinatal ? (React.createElement(React.Fragment, null,
44
+ React.createElement("div", { className: "font-[900] bold-text whitespace-nowrap" }, isLinatal ? (React.createElement(React.Fragment, null,
45
45
  cleanedDepTime,
46
46
  " ",
47
47
  React.createElement("span", null, hasPM ? "PM" : hasAM ? "AM" : ""),
@@ -49,7 +49,7 @@ function DateTimeSection({ serviceItem, isSoldOut, isCiva, isLinatal, removeArri
49
49
  !(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dep_time.includes("PM")) &&
50
50
  DateService.ampmOnly(serviceItem.dep_time))) : (DateService.formatTime(serviceItem.dep_time)))),
51
51
  !isCiva && (React.createElement(StageTooltip, { stageData: serviceItem.dropoff_stages, direction: 2, terminals: busStage, serviceItem: serviceItem, metaData: metaData, colors: colors },
52
- React.createElement("div", { className: "font-[900] bold-text" }, removeArrivalTime
52
+ React.createElement("div", { className: "font-[900] bold-text whitespace-nowrap" }, removeArrivalTime
53
53
  ? "\u00A0"
54
54
  : serviceItem.arr_time
55
55
  ? DateService.formatTime(serviceItem.arr_time)
@@ -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,7 +18,11 @@ 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
  tooltipColor?: string;
20
26
  }
21
- declare function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, tooltipColor, }: SeatSectionProps): React.ReactElement;
27
+ declare function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, selectedSeatKey, onSeatSelect, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, isTrain, topLabelColor, tooltipColor, }: SeatSectionProps): React.ReactElement;
22
28
  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,11 +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, tooltipColor, }) {
61
+ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, selectedSeatKey, onSeatSelect, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, isTrain, topLabelColor, tooltipColor, }) {
58
62
  var _a;
59
63
  const uniqueSeats = getUniqueSeats(seatTypes);
60
- const sortedSeatTypes = getSortedSeatTypes(seatTypes);
61
- console.log("🚀 ~ SeatSection ~ sortedSeatTypes:", sortedSeatTypes);
64
+ const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
62
65
  const numberOfSeats = getNumberOfSeats(seatTypes);
63
66
  const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
64
67
  const formatPrice = (price) => availableSeats <= 0
@@ -66,11 +69,36 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
66
69
  : CommonService.currency(price, currencySign);
67
70
  const renderSeatNames = () => {
68
71
  const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
69
- 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"
70
- ? removeDuplicateSeats && isPeru
71
- ? CommonService.truncateSeatLabel(val.label)
72
- : val.label
73
- : 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] whitespace-nowrap ${isSoldOut ? "text-[#c0c0c0]" : ""}` }, typeof val.label === "string" || typeof val.label === "number"
95
+ ? removeDuplicateSeats && isPeru
96
+ ? CommonService.truncateSeatLabel(val.label)
97
+ : isTrain
98
+ ? CommonService.capitalize(String(val.label))
99
+ : val.label
100
+ : null)));
101
+ });
74
102
  };
75
103
  const renderSeatPrices = () => {
76
104
  if (isPeru) {
@@ -126,6 +154,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
126
154
  }
127
155
  return null;
128
156
  })();
157
+ console.log("🚀 ~ SeatSection ~ serviceItem:", serviceItem);
129
158
  // Hide the % OFF badge when max_discount is capping the percentage discount
130
159
  // (i.e. both percentage and max_discount exist, and the raw % amount exceeds the cap)
131
160
  const isMaxDiscountApplied = (() => {
@@ -162,7 +191,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
162
191
  hasDiscount && (React.createElement("span", { className: "text-[13.33px]", style: {
163
192
  color: "#999",
164
193
  } }, "Antes")),
165
- React.createElement("span", { className: "text-[13.33px] flex flex-col" }, operatorServiceName ? (React.createElement("span", { className: "text-[13.33px]" }, seatLabel)) : (React.createElement("span", { className: "text-[13.33px]" }, "Desde")))));
194
+ React.createElement("span", { className: "text-[13.33px] flex flex-col" }, operatorServiceName ? (React.createElement("span", { className: "text-[13.33px] whitespace-nowrap" }, seatLabel)) : (React.createElement("span", { className: "text-[13.33px]" }, "Desde")))));
166
195
  }
167
196
  return renderSeatNames();
168
197
  };
@@ -264,7 +293,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
264
293
  discountValue,
265
294
  "% OFF"))),
266
295
  React.createElement("div", { className: "col-start-2 row-start-2 flex items-center justify-center ", style: { textAlign: "center" } },
267
- React.createElement("span", { className: "text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative", style: {
296
+ React.createElement("span", { className: "text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative whitespace-nowrap", style: {
268
297
  position: "relative",
269
298
  } },
270
299
  formatPrice(discountSeat.originalPrice),
@@ -294,7 +323,9 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
294
323
  color: isSoldOut ? "#c0c0c0" : priceColor,
295
324
  top: 0,
296
325
  bottom: 0,
297
- left: "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
326
+ left: isTrain
327
+ ? "73%"
328
+ : "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
298
329
  right: 0,
299
330
  justifyContent: isCentered ? "center" : "",
300
331
  gap: "10px",
@@ -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,19 +23,21 @@ 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
  " ",
31
33
  label ? (React.createElement("div", { className: "w-[60px]" }, label)) : (React.createElement("div", { className: "w-[12px] h-auto mr-[5px]" },
32
34
  React.createElement("img", { src: icon, alt: alt, className: `w-[12px] h-auto mr-[5px] ${isSoldOut ? "grayscale" : ""}` })))),
33
35
  React.createElement("div", { className: "flex items-center relative capitalize justify-between", style: { flex: 1 } },
34
- React.createElement("span", { className: "cursor-pointer black-text" }, formattedDate),
36
+ React.createElement("span", { className: "cursor-pointer black-text whitespace-nowrap" }, 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 whitespace-nowrap" }, 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(commonService.capitalize(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) {
@@ -72,7 +74,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
72
74
  return (React.createElement(React.Fragment, null, uniqueSeats.map((seat, key) => {
73
75
  const { discountedPrice } = commonService.calculateDiscountedPrice(Number(seat.fare), serviceItem);
74
76
  return (React.createElement("div", { key: key, className: "w-[100%] flex flex-row justify-between items-center" },
75
- React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: isSoldOut ? "#bbb" : "#464647" } }, commonService.truncateSeatLabel(seat.label)),
77
+ React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text whitespace-nowrap", style: { color: isSoldOut ? "#bbb" : "#464647" } }, commonService.truncateSeatLabel(seat.label)),
76
78
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(discountedPrice, currencySign))));
77
79
  })));
78
80
  }
@@ -117,19 +119,24 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
117
119
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] text-[#464647]", style: { opacity: isSoldOut ? 0.5 : 1 } }, "Agotado"))) : null));
118
120
  };
119
121
  const renderSeats = () => {
120
- var _a, _b, _c;
122
+ var _a, _b;
121
123
  if (isPeru) {
122
124
  return renderPeruSeats();
123
125
  }
124
126
  if (removeDuplicateSeats) {
125
127
  const uniqueSeats = getUniqueSeats(seatTypesData, 3);
126
- 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]" })));
128
+ 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 })));
127
129
  }
128
- 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]" })));
130
+ 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);
131
+ 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 })));
129
132
  };
130
133
  const seats = removeDuplicateSeats
131
134
  ? getUniqueSeats(seatTypesData, 3)
132
- : (_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);
135
+ : (() => {
136
+ var _a;
137
+ 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);
138
+ return isTrain ? filtered : filtered === null || filtered === void 0 ? void 0 : filtered.slice(0, 2);
139
+ })();
133
140
  const discountedSeats = seats === null || seats === void 0 ? void 0 : seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
134
141
  const peruLowestFare = isPeru ? getLowestFare() : null;
135
142
  const peruDiscountCalc = isPeru && peruLowestFare != null
@@ -140,7 +147,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
140
147
  peruDiscountCalc.originalPrice !== peruDiscountCalc.discountedPrice
141
148
  : discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
142
149
  const discountSeat = isPeru && peruDiscountCalc
143
- ? 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];
150
+ ? 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];
144
151
  const discountValue = (() => {
145
152
  if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_type) === "percentage" &&
146
153
  typeof (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) === "number") {
@@ -176,14 +183,14 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
176
183
  };
177
184
  const originalDpPrice = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.original_dp_price);
178
185
  const dpDiscountPercent = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents);
179
- 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;
186
+ 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;
180
187
  const hasDpDiscount = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) &&
181
188
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) &&
182
189
  originalDpPrice != null &&
183
190
  dpDiscountPercent != null &&
184
191
  firstSeatFare != null;
185
192
  return (React.createElement("div", { className: "content-center relative", style: { width: "40%" } }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
186
- !((_g = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _g === void 0 ? void 0 : _g.length) &&
193
+ !((_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _e === void 0 ? void 0 : _e.length) &&
187
194
  !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] " },
188
195
  !isNaN(Number(dpDiscountPercent)) &&
189
196
  Number(dpDiscountPercent) > 0 && (React.createElement("div", { className: "absolute -top-[18px] right-[0px]", style: {
@@ -215,7 +222,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
215
222
  React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
216
223
  color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
217
224
  } },
218
- ((_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,
225
+ ((_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,
219
226
  commonService.discountedCurrency(Number(firstSeatFare), currencySign)),
220
227
  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,
221
228
  React.createElement("div", { className: "relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] " },
@@ -249,8 +256,8 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
249
256
  React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
250
257
  color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
251
258
  } },
252
- ((_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,
253
- commonService.discountedCurrency(discountSeat.discountedPrice, currencySign))))) : (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem] ", style: {
259
+ ((_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,
260
+ commonService.discountedCurrency(discountSeat.discountedPrice, currencySign))))) : (React.createElement("div", { className: `flex flex-col justify-between ${isTrain ? "" : "h-[2.5rem]"} `, style: {
254
261
  gap: isSoldOut ? "0px" : "5px",
255
262
  justifyContent: hasMultipleTypes ? "space-between" : "center",
256
263
  } },
@@ -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.11.2",
3
+ "version": "9.11.3",
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,
@@ -145,7 +152,15 @@ function ServiceItemPB({
145
152
  isFlores,
146
153
  operatorLabel,
147
154
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
148
- console.log("🚀 ~ ServiceItemPB ~ serviceItem:", serviceItem);
155
+ const handleSeatSelect = (
156
+ key: any,
157
+ price: number,
158
+ seatKey: string,
159
+ apiSeatType?: string,
160
+ ) => {
161
+ onClearSeatSelectionError?.();
162
+ onSeatSelect?.(key, price, seatKey, apiSeatType);
163
+ };
149
164
  const getAnimationIcon = (icon: string) => {
150
165
  const animation = ANIMATION_MAP[icon];
151
166
  if (!animation) return null;
@@ -367,6 +382,17 @@ function ServiceItemPB({
367
382
  return;
368
383
  }
369
384
 
385
+ if (isTrain) {
386
+ if (!selectedSeatKey) {
387
+ onShowSeatSelectionError?.(serviceItem.id);
388
+ return;
389
+ }
390
+ if (onTrainButtonClick) {
391
+ onTrainButtonClick();
392
+ return;
393
+ }
394
+ }
395
+
370
396
  onBookButtonPress();
371
397
  };
372
398
 
@@ -559,7 +585,9 @@ function ServiceItemPB({
559
585
  : "",
560
586
  }}
561
587
  >
562
- <div className="grid text-[#464647] w-full [grid-template-columns:20%_30%_2.5%_24%_15.5%] gap-x-[2%] items-center">
588
+ <div
589
+ className={`grid text-[#464647] w-full ${isTrain ? "[grid-template-columns:16%_30%_2.5%_28%_15.5%]" : "[grid-template-columns:20%_30%_2.5%_24%_15.5%]"} gap-x-[2%] items-center`}
590
+ >
563
591
  {/* OPERATOR LOGO */}
564
592
  <div className="flex flex-col gap-[5px]">
565
593
  <div>
@@ -621,6 +649,10 @@ function ServiceItemPB({
621
649
  isPeru={isPeru}
622
650
  renderIcon={renderIcon}
623
651
  discountSeatPriceColor={colors.discountSeatPriceColor}
652
+ isTrain={isTrain}
653
+ selectedSeatKey={selectedSeatKey}
654
+ onSeatSelect={handleSeatSelect}
655
+ topLabelColor={colors.topLabelColor}
624
656
  tooltipColor={colors.tooltipColor}
625
657
  />
626
658
  </div>
@@ -637,6 +669,18 @@ function ServiceItemPB({
637
669
  soldOutIcon={renderIcon("soldOutIcon", "14px")}
638
670
  onClick={checkMidnight}
639
671
  />
672
+ {showSeatSelectionError === serviceItem.id && isTrain && (
673
+ <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
674
+ <div
675
+ className="text-[9px] text-center whitespace-nowrap"
676
+ style={{
677
+ color: colors.seatPriceColor,
678
+ }}
679
+ >
680
+ Selecciona el tipo de servicio
681
+ </div>
682
+ </div>
683
+ )}
640
684
  {showLastSeats ? (
641
685
  <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
642
686
  {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,
@@ -61,7 +63,6 @@ function ServiceItemMobile({
61
63
  onTimeDropdownToggle,
62
64
  wowDealData,
63
65
  isFlores,
64
- operatorLabel
65
66
  }: MobileServiceItemProps): React.ReactElement {
66
67
  const isItemExpanded = serviceItem.id === isExpanded;
67
68
  const isPetSeat = (Object.keys(serviceItem?.pet_seat_info) || []).length > 0;
@@ -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,28 @@ 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
+ isTrain?: boolean;
232
+ isFeatureDropDownExpand?: any;
232
233
  setIsFeatureDropDownExpand?: (value: any) => void;
233
234
  ticketQuantity?: number;
234
- onIncreaseTicketQuantity?: (serviceItem: MobileServiceItemProps["serviceItem"]) => void;
235
- onDecreaseTicketQuantity?: (serviceItem: MobileServiceItemProps["serviceItem"]) => void;
235
+ onIncreaseTicketQuantity?: (
236
+ serviceItem: MobileServiceItemProps["serviceItem"],
237
+ ) => void;
238
+ onDecreaseTicketQuantity?: (
239
+ serviceItem: MobileServiceItemProps["serviceItem"],
240
+ ) => void;
236
241
  cityOrigin?: { value: number; label: string };
237
242
  cityDestination?: { value: number; label: string };
238
- isNewUi?: boolean
243
+ isNewUi?: boolean;
239
244
 
240
- selectedTimeSlot?: string;
245
+ selectedTimeSlot?: string;
241
246
  onTimeSlotChange?: (slot: string) => void;
242
247
  isTimeDropdownOpen?: string | number | null;
243
248
  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;
@@ -119,7 +119,7 @@ function DateTimeSection({
119
119
  metaData={metaData}
120
120
  colors={colors}
121
121
  >
122
- <span className="cursor-pointer bold-text capitalize">
122
+ <span className="cursor-pointer bold-text capitalize whitespace-nowrap">
123
123
  {DateService.getServiceItemDate(serviceItem.travel_date)}
124
124
  </span>
125
125
  </StageTooltip>
@@ -134,7 +134,7 @@ function DateTimeSection({
134
134
  metaData={metaData}
135
135
  colors={colors}
136
136
  >
137
- <span className="cursor-pointer bold-text capitalize">
137
+ <span className="cursor-pointer bold-text capitalize whitespace-nowrap">
138
138
  {DateService.getServiceItemDate(serviceItem.arrival_date)}
139
139
  </span>
140
140
  </StageTooltip>
@@ -169,7 +169,7 @@ function DateTimeSection({
169
169
  metaData={metaData}
170
170
  colors={colors}
171
171
  >
172
- <div className="font-[900] bold-text">
172
+ <div className="font-[900] bold-text whitespace-nowrap">
173
173
  {isLinatal ? (
174
174
  <>
175
175
  {cleanedDepTime} <span>{hasPM ? "PM" : hasAM ? "AM" : ""}</span>
@@ -193,7 +193,7 @@ function DateTimeSection({
193
193
  metaData={metaData}
194
194
  colors={colors}
195
195
  >
196
- <div className="font-[900] bold-text">
196
+ <div className="font-[900] bold-text whitespace-nowrap">
197
197
  {removeArrivalTime
198
198
  ? "\u00A0"
199
199
  : serviceItem.arr_time
@@ -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
  tooltipColor?: string;
25
36
  }
26
37
 
@@ -32,6 +43,8 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
32
43
  let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
33
44
  label: val?.label,
34
45
  price: val?.fare,
46
+ key: val?.key,
47
+ apiSeatType: val?.apiSeatType || val?.api_seat_type,
35
48
  }));
36
49
 
37
50
  seatTypesWithPrices.sort((a, b) => a.price - b.price);
@@ -39,7 +52,7 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
39
52
  return seatTypesWithPrices;
40
53
  }
41
54
 
42
- function getSortedSeatTypes(seatTypes: SeatType[]) {
55
+ function getSortedSeatTypes(seatTypes: SeatType[], isTrain: any) {
43
56
  if (!seatTypes?.length) {
44
57
  return [{ label: "Salon cama", price: 0 }];
45
58
  }
@@ -53,7 +66,9 @@ function getSortedSeatTypes(seatTypes: SeatType[]) {
53
66
  seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
54
67
  }
55
68
 
56
- seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
69
+ if (!isTrain) {
70
+ seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
71
+ }
57
72
 
58
73
  const seenPrices = new Set<number>();
59
74
  seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
@@ -98,16 +113,19 @@ function SeatSection({
98
113
  priceColor,
99
114
  currencySign,
100
115
  removeDuplicateSeats,
116
+ selectedSeatKey,
117
+ onSeatSelect,
101
118
  isPeru,
102
119
  serviceItem,
103
120
  renderIcon,
104
121
  dpSeatColor,
105
122
  discountSeatPriceColor,
123
+ isTrain,
124
+ topLabelColor,
106
125
  tooltipColor,
107
126
  }: SeatSectionProps): React.ReactElement {
108
127
  const uniqueSeats = getUniqueSeats(seatTypes);
109
- const sortedSeatTypes = getSortedSeatTypes(seatTypes);
110
- console.log("🚀 ~ SeatSection ~ sortedSeatTypes:", sortedSeatTypes);
128
+ const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
111
129
  const numberOfSeats = getNumberOfSeats(seatTypes);
112
130
  const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
113
131
 
@@ -119,22 +137,68 @@ function SeatSection({
119
137
  const renderSeatNames = () => {
120
138
  const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
121
139
 
122
- return seats.map((val, key: number) =>
123
- SEAT_EXCEPTIONS.includes(val.label) ? null : (
124
- <span
125
- key={key}
126
- className={`flex items-center justify-between text-[13.33px] ${
127
- isSoldOut ? "text-[#c0c0c0]" : ""
128
- }`}
140
+ return seats.map((val, key: number) => {
141
+ return SEAT_EXCEPTIONS.includes(val.label) ? null : (
142
+ <div
143
+ className="flex items-center"
144
+ style={isTrain ? { cursor: "pointer" } : undefined}
145
+ onClick={
146
+ isTrain && !isSoldOut
147
+ ? () =>
148
+ val.label === selectedSeatKey
149
+ ? onSeatSelect?.(null, 0, "", "")
150
+ : onSeatSelect?.(
151
+ val.label,
152
+ val.price,
153
+ val.key,
154
+ (val as any).apiSeatType,
155
+ )
156
+ : undefined
157
+ }
129
158
  >
130
- {typeof val.label === "string" || typeof val.label === "number"
131
- ? removeDuplicateSeats && isPeru
132
- ? CommonService.truncateSeatLabel(val.label)
133
- : val.label
134
- : null}
135
- </span>
136
- ),
137
- );
159
+ {isTrain && (
160
+ <div
161
+ style={{
162
+ border: `1px solid ${val.label === selectedSeatKey ? topLabelColor : "#ccc"}`,
163
+ borderRadius: "50%",
164
+ width: "14px",
165
+ height: "14px",
166
+ minWidth: "14px",
167
+ marginRight: "10px",
168
+ display: "flex",
169
+ alignItems: "center",
170
+ justifyContent: "center",
171
+ }}
172
+ >
173
+ {val.label === selectedSeatKey && (
174
+ <div
175
+ style={{
176
+ backgroundColor: topLabelColor,
177
+ borderRadius: "50%",
178
+ width: "7px",
179
+ height: "7px",
180
+ }}
181
+ />
182
+ )}
183
+ </div>
184
+ )}
185
+ <span
186
+ key={key}
187
+ className={`flex items-center justify-between text-[13.33px] whitespace-nowrap ${
188
+ isSoldOut ? "text-[#c0c0c0]" : ""
189
+ }`}
190
+ >
191
+ {typeof val.label === "string" || typeof val.label === "number"
192
+ ? removeDuplicateSeats && isPeru
193
+ ? CommonService.truncateSeatLabel(val.label)
194
+ : isTrain
195
+ ? CommonService.capitalize(String(val.label))
196
+ : val.label
197
+ : null}
198
+ </span>
199
+ </div>
200
+ );
201
+ });
138
202
  };
139
203
 
140
204
  const renderSeatPrices = () => {
@@ -151,7 +215,10 @@ function SeatSection({
151
215
  serviceItem,
152
216
  );
153
217
  return (
154
- <span key={key} className="flex items-center text-[13.33px] bold-text">
218
+ <span
219
+ key={key}
220
+ className="flex items-center text-[13.33px] bold-text"
221
+ >
155
222
  {formatPrice(discountedPrice)}
156
223
  </span>
157
224
  );
@@ -235,6 +302,7 @@ function SeatSection({
235
302
  }
236
303
  return null;
237
304
  })();
305
+ console.log("🚀 ~ SeatSection ~ serviceItem:", serviceItem);
238
306
 
239
307
  // Hide the % OFF badge when max_discount is capping the percentage discount
240
308
  // (i.e. both percentage and max_discount exist, and the raw % amount exceeds the cap)
@@ -304,7 +372,9 @@ function SeatSection({
304
372
 
305
373
  <span className="text-[13.33px] flex flex-col">
306
374
  {operatorServiceName ? (
307
- <span className="text-[13.33px]">{seatLabel}</span>
375
+ <span className="text-[13.33px] whitespace-nowrap">
376
+ {seatLabel}
377
+ </span>
308
378
  ) : (
309
379
  <span className="text-[13.33px]">Desde</span>
310
380
  )}
@@ -527,7 +597,7 @@ function SeatSection({
527
597
  style={{ textAlign: "center" }}
528
598
  >
529
599
  <span
530
- className="text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative"
600
+ className="text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative whitespace-nowrap"
531
601
  style={{
532
602
  position: "relative",
533
603
  }}
@@ -589,7 +659,9 @@ function SeatSection({
589
659
  color: isSoldOut ? "#c0c0c0" : priceColor,
590
660
  top: 0,
591
661
  bottom: 0,
592
- left: "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
662
+ left: isTrain
663
+ ? "73%"
664
+ : "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
593
665
  right: 0,
594
666
  justifyContent: isCentered ? "center" : "",
595
667
  gap: "10px",
@@ -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,47 @@ 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
- />
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 whitespace-nowrap">
102
+ {formattedDate}
103
+ </span>
104
+ <div className={`absolute left-[50%] ${dotPositionClass}`}>•</div>
105
+ <div className="font-[900] relative black-text whitespace-nowrap">
106
+ {timeContent}
90
107
  </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>
108
+ </div>
102
109
  </div>
103
110
  </div>
104
- </div>;
111
+ );
105
112
  };
106
113
 
107
114
  function DateTimeSectionMobile({
@@ -127,6 +134,7 @@ function DateTimeSectionMobile({
127
134
  tooltipBgColor,
128
135
  showLastSeats,
129
136
  discountSeatPriceColor,
137
+ isTrain,
130
138
  }: DateTimeSectionMobileProps): React.ReactElement {
131
139
  const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
132
140
 
@@ -153,8 +161,12 @@ function DateTimeSectionMobile({
153
161
  >
154
162
  {/* DATE AND TIME */}
155
163
  <div
156
- className="min-h-[2.5rem] flex flex-col justify-between gap-[4px] w-[50%] "
157
- style={{ justifyContent: isCiva && "center" }}
164
+ className={`flex flex-col gap-[4px] w-[50%] ${isTrain ? "justify-center" : "justify-between"}`}
165
+ style={{
166
+ justifyContent: isCiva && "center",
167
+ minHeight: isTrain ? undefined : "2.5rem",
168
+ alignSelf: isTrain ? "stretch" : undefined,
169
+ }}
158
170
  >
159
171
  <TimeRow
160
172
  label={orignLabel}
@@ -198,6 +210,7 @@ function DateTimeSectionMobile({
198
210
  tooltipBgColor={tooltipBgColor}
199
211
  showLastSeats={showLastSeats}
200
212
  discountSeatPriceColor={discountSeatPriceColor}
213
+ isTrain={isTrain}
201
214
  />
202
215
  </div>
203
216
  );
@@ -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,12 @@ 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(
77
+ commonService.capitalize(displayLabel),
78
+ 8,
79
+ )
80
+ : displayLabel}
73
81
  </span>
74
82
  <span
75
83
  className={`min-[420]:text-[13px] ${textSize} bold-text`}
@@ -118,6 +126,7 @@ function SeatSectionMobile({
118
126
  tooltipBgColor,
119
127
  showLastSeats,
120
128
  discountSeatPriceColor,
129
+ isTrain,
121
130
  }: SeatSectionMobileProps): React.ReactElement {
122
131
  const hasMultipleTypes = (seatTypesData?.length ?? 0) > 2;
123
132
 
@@ -169,7 +178,7 @@ function SeatSectionMobile({
169
178
  className="w-[100%] flex flex-row justify-between items-center"
170
179
  >
171
180
  <span
172
- className="min-[420]:text-[13px] text-[12px] bold-text"
181
+ className="min-[420]:text-[13px] text-[12px] bold-text whitespace-nowrap"
173
182
  style={{ color: isSoldOut ? "#bbb" : "#464647" }}
174
183
  >
175
184
  {commonService.truncateSeatLabel(seat.label)}
@@ -322,15 +331,17 @@ function SeatSectionMobile({
322
331
  seatPriceColor={seatPriceColor}
323
332
  hasMultipleTypes={hasMultipleTypes}
324
333
  textSize="text-[11px]"
334
+ isTrain={isTrain}
325
335
  />
326
336
  ));
327
337
  }
328
338
 
329
- return seatTypesData
339
+ const filteredSeats = seatTypesData
330
340
  ?.filter((item) => getFilteredSeats(item.label))
331
- ?.sort((a, b) => a.fare - b.fare)
332
- ?.slice(0, 2)
333
- ?.map((type, i) => (
341
+ ?.sort((a, b) => a.fare - b.fare);
342
+
343
+ return (isTrain ? filteredSeats : filteredSeats?.slice(0, 2))?.map(
344
+ (type, i) => (
334
345
  <SeatRow
335
346
  key={i}
336
347
  type={type}
@@ -341,16 +352,20 @@ function SeatSectionMobile({
341
352
  seatPriceColor={seatPriceColor}
342
353
  hasMultipleTypes={hasMultipleTypes}
343
354
  textSize="text-[12px]"
355
+ isTrain={isTrain}
344
356
  />
345
- ));
357
+ ),
358
+ );
346
359
  };
347
360
 
348
361
  const seats = removeDuplicateSeats
349
362
  ? getUniqueSeats(seatTypesData, 3)
350
- : seatTypesData
351
- ?.filter((item) => getFilteredSeats(item.label))
352
- ?.sort((a, b) => a.fare - b.fare)
353
- ?.slice(0, 2);
363
+ : (() => {
364
+ const filtered = seatTypesData
365
+ ?.filter((item) => getFilteredSeats(item.label))
366
+ ?.sort((a, b) => a.fare - b.fare);
367
+ return isTrain ? filtered : filtered?.slice(0, 2);
368
+ })();
354
369
 
355
370
  const discountedSeats = seats?.map((seat) => ({
356
371
  ...seat,
@@ -602,7 +617,7 @@ function SeatSectionMobile({
602
617
  </div>
603
618
  ) : (
604
619
  <div
605
- className="flex flex-col justify-between h-[2.5rem] "
620
+ className={`flex flex-col justify-between ${isTrain ? "" : "h-[2.5rem]"} `}
606
621
  style={{
607
622
  gap: isSoldOut ? "0px" : "5px",
608
623
  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) =>