kupos-ui-components-lib 9.11.7 → 9.11.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.
Files changed (32) hide show
  1. package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
  2. package/dist/components/ServiceItem/ServiceItemDesktop.js +25 -7
  3. package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
  4. package/dist/components/ServiceItem/ServiceItemMobile.js +1 -1
  5. package/dist/components/ServiceItem/mobileTypes.d.ts +1 -0
  6. package/dist/components/ServiceItem/types.d.ts +7 -0
  7. package/dist/styles.css +6 -0
  8. package/dist/ui/BottomAmenities/BottomAmenities.d.ts +2 -1
  9. package/dist/ui/BottomAmenities/BottomAmenities.js +4 -2
  10. package/dist/ui/DateTimeSection/DateTimeSection.js +4 -4
  11. package/dist/ui/OfferBanner.d.ts +1 -0
  12. package/dist/ui/OfferBanner.js +6 -2
  13. package/dist/ui/SeatSection/SeatSection.d.ts +7 -1
  14. package/dist/ui/SeatSection/SeatSection.js +43 -12
  15. package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +2 -1
  16. package/dist/ui/mobileweb/DateTimeSectionMobile.js +13 -7
  17. package/dist/ui/mobileweb/SeatSectionMobile.d.ts +2 -1
  18. package/dist/ui/mobileweb/SeatSectionMobile.js +22 -15
  19. package/dist/utils/CommonService.d.ts +1 -1
  20. package/dist/utils/CommonService.js +5 -1
  21. package/package.json +1 -1
  22. package/src/components/ServiceItem/ServiceItemDesktop.tsx +51 -5
  23. package/src/components/ServiceItem/ServiceItemMobile.tsx +2 -1
  24. package/src/components/ServiceItem/mobileTypes.ts +31 -26
  25. package/src/components/ServiceItem/types.ts +12 -0
  26. package/src/ui/BottomAmenities/BottomAmenities.tsx +5 -1
  27. package/src/ui/DateTimeSection/DateTimeSection.tsx +4 -4
  28. package/src/ui/OfferBanner.tsx +7 -1
  29. package/src/ui/SeatSection/SeatSection.tsx +90 -21
  30. package/src/ui/mobileweb/DateTimeSectionMobile.tsx +47 -34
  31. package/src/ui/mobileweb/SeatSectionMobile.tsx +27 -12
  32. package/src/utils/CommonService.ts +7 -1
@@ -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.7",
3
+ "version": "9.11.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,
@@ -145,6 +152,15 @@ function ServiceItemPB({
145
152
  isFlores,
146
153
  operatorLabel,
147
154
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
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
+ };
148
164
  const getAnimationIcon = (icon: string) => {
149
165
  const animation = ANIMATION_MAP[icon];
150
166
  if (!animation) return null;
@@ -366,6 +382,17 @@ function ServiceItemPB({
366
382
  return;
367
383
  }
368
384
 
385
+ if (isTrain) {
386
+ if (!selectedSeatKey) {
387
+ onShowSeatSelectionError?.(serviceItem.id);
388
+ return;
389
+ }
390
+ if (onTrainButtonClick) {
391
+ onTrainButtonClick();
392
+ return;
393
+ }
394
+ }
395
+
369
396
  onBookButtonPress();
370
397
  };
371
398
 
@@ -530,6 +557,7 @@ function ServiceItemPB({
530
557
  showLoginOption={showLoginOption}
531
558
  isNewUiEnabled={isNewUiEnabled}
532
559
  colors={colors}
560
+ isLinatal={isLinatal}
533
561
  />
534
562
  )}
535
563
  <div
@@ -558,7 +586,9 @@ function ServiceItemPB({
558
586
  : "",
559
587
  }}
560
588
  >
561
- <div className="grid text-[#464647] w-full [grid-template-columns:20%_30%_2.5%_24%_15.5%] gap-x-[2%] items-center">
589
+ <div
590
+ 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`}
591
+ >
562
592
  {/* OPERATOR LOGO */}
563
593
  <div className="flex flex-col gap-[5px]">
564
594
  <div>
@@ -620,6 +650,10 @@ function ServiceItemPB({
620
650
  isPeru={isPeru}
621
651
  renderIcon={renderIcon}
622
652
  discountSeatPriceColor={colors.discountSeatPriceColor}
653
+ isTrain={isTrain}
654
+ selectedSeatKey={selectedSeatKey}
655
+ onSeatSelect={handleSeatSelect}
656
+ topLabelColor={colors.topLabelColor}
623
657
  tooltipColor={colors.tooltipColor}
624
658
  />
625
659
  </div>
@@ -636,6 +670,18 @@ function ServiceItemPB({
636
670
  soldOutIcon={renderIcon("soldOutIcon", "14px")}
637
671
  onClick={checkMidnight}
638
672
  />
673
+ {showSeatSelectionError === serviceItem.id && isTrain && (
674
+ <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
675
+ <div
676
+ className="text-[9px] text-center whitespace-nowrap"
677
+ style={{
678
+ color: colors.seatPriceColor,
679
+ }}
680
+ >
681
+ Selecciona el tipo de servicio
682
+ </div>
683
+ </div>
684
+ )}
639
685
  {showLastSeats ? (
640
686
  <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
641
687
  {serviceItem?.available_seats < 10 &&
@@ -668,6 +714,7 @@ function ServiceItemPB({
668
714
  setIsExpand(isItemExpanded ? null : serviceItem.id)
669
715
  }
670
716
  isPeru={isPeru}
717
+ isTrain={isTrain}
671
718
  />
672
719
  </div>
673
720
  </div>
@@ -681,7 +728,7 @@ function ServiceItemPB({
681
728
  transition:
682
729
  "grid-template-rows 0.3s ease-in-out, opacity 0.25s ease-in-out",
683
730
  position: "relative",
684
- zIndex: hasOfferText || hasDpEnabled ? 0 : -1,
731
+ zIndex: hasDiscount || hasDpEnabled ? 0 : -1,
685
732
  marginTop: isItemExpanded ? "" : "-10px",
686
733
  }}
687
734
  >
@@ -689,9 +736,8 @@ function ServiceItemPB({
689
736
  style={{
690
737
  overflow: "hidden",
691
738
  minHeight: 0,
692
- marginTop:
693
- hasDpEnabled || serviceItem?.offer_text ? "" : "-10px",
694
- ...(hasOfferText || hasDpEnabled
739
+ marginTop: hasDpEnabled || hasDiscount ? "" : "-10px",
740
+ ...(hasDpEnabled || hasDiscount
695
741
  ? {
696
742
  borderLeft: isSoldOut
697
743
  ? ""
@@ -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;
@@ -28,6 +28,7 @@ interface BottomAmenitiesProps {
28
28
  downArrowIcon?: React.ReactNode;
29
29
  onToggleExpand: () => void;
30
30
  isPeru?: boolean;
31
+ isTrain?: boolean;
31
32
  }
32
33
 
33
34
  function BottomAmenities({
@@ -42,6 +43,7 @@ function BottomAmenities({
42
43
  downArrowIcon,
43
44
  onToggleExpand,
44
45
  isPeru,
46
+ isTrain,
45
47
  }: BottomAmenitiesProps): React.ReactElement {
46
48
  const hasPetInfo =
47
49
  serviceItem.pet_seat_info &&
@@ -59,7 +61,9 @@ function BottomAmenities({
59
61
  className="grid items-center gap-[2%] flex-1 "
60
62
  style={{
61
63
  // gridTemplateColumns: " 28% 21% 23% 23%",
62
- gridTemplateColumns: "25.3% 17% 23% 23%",
64
+ gridTemplateColumns: isTrain
65
+ ? "20.6% 17% 23% 23%"
66
+ : "25.3% 17% 23% 23%",
63
67
  }}
64
68
  >
65
69
  {otherItems.map((item) => (
@@ -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
@@ -29,6 +29,7 @@ interface OfferBannerProps {
29
29
  showLoginOption?: boolean;
30
30
  isNewUiEnabled?: boolean;
31
31
  colors: OfferBannerColors;
32
+ isLinatal?: boolean;
32
33
  }
33
34
 
34
35
  // ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -206,6 +207,7 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
206
207
  showLoginOption,
207
208
  isNewUiEnabled,
208
209
  colors,
210
+ isLinatal,
209
211
  }) => {
210
212
  const isLegacyOffer =
211
213
  !!serviceItem?.offer_text && !isNewUiEnabled && !serviceItem?.is_dp_enabled;
@@ -250,7 +252,11 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
250
252
  return (
251
253
  <div
252
254
  className="text-white p-[10px_15px] text-left w-full flex items-center absolute -bottom-[44px] pt-[50px] rounded-b-[14px] text-[14px] mt-[10px]"
253
- style={{ background, opacity: isSoldOut ? 0.5 : 1 }}
255
+ style={{
256
+ background,
257
+ opacity: isSoldOut ? 0.5 : 1,
258
+ zIndex: isLinatal ? "-1" : "",
259
+ }}
254
260
  >
255
261
  <div className="flex justify-between items-center w-full">
256
262
  <div className="flex items-center">{renderLeftContent()}</div>
@@ -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,15 +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);
128
+ const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
110
129
  const numberOfSeats = getNumberOfSeats(seatTypes);
111
130
  const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
112
131
 
@@ -118,22 +137,68 @@ function SeatSection({
118
137
  const renderSeatNames = () => {
119
138
  const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
120
139
 
121
- return seats.map((val, key: number) =>
122
- SEAT_EXCEPTIONS.includes(val.label) ? null : (
123
- <span
124
- key={key}
125
- className={`flex items-center justify-between text-[13.33px] ${
126
- isSoldOut ? "text-[#c0c0c0]" : ""
127
- }`}
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
+ }
128
158
  >
129
- {typeof val.label === "string" || typeof val.label === "number"
130
- ? removeDuplicateSeats && isPeru
131
- ? CommonService.truncateSeatLabel(val.label)
132
- : val.label
133
- : null}
134
- </span>
135
- ),
136
- );
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
+ });
137
202
  };
138
203
 
139
204
  const renderSeatPrices = () => {
@@ -310,7 +375,9 @@ function SeatSection({
310
375
 
311
376
  <span className="text-[13.33px] flex flex-col">
312
377
  {operatorServiceName ? (
313
- <span className="text-[13.33px]">{seatLabel}</span>
378
+ <span className="text-[13.33px] whitespace-nowrap">
379
+ {seatLabel}
380
+ </span>
314
381
  ) : (
315
382
  <span className="text-[13.33px]">Desde</span>
316
383
  )}
@@ -533,7 +600,7 @@ function SeatSection({
533
600
  style={{ textAlign: "center" }}
534
601
  >
535
602
  <span
536
- className="text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative"
603
+ className="text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative whitespace-nowrap"
537
604
  style={{
538
605
  position: "relative",
539
606
  }}
@@ -595,7 +662,9 @@ function SeatSection({
595
662
  color: isSoldOut ? "#c0c0c0" : priceColor,
596
663
  top: 0,
597
664
  bottom: 0,
598
- left: "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
665
+ left: isTrain
666
+ ? "73%"
667
+ : "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
599
668
  right: 0,
600
669
  justifyContent: isCentered ? "center" : "",
601
670
  gap: "10px",