kupos-ui-components-lib 9.9.7 → 9.9.8

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 (42) hide show
  1. package/dist/KuposUIComponent.d.ts +3 -0
  2. package/dist/components/ServiceItem/PeruServiceItemDesktop.d.ts +1 -1
  3. package/dist/components/ServiceItem/PeruServiceItemDesktop.js +156 -176
  4. package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
  5. package/dist/components/ServiceItem/ServiceItemDesktop.js +29 -31
  6. package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
  7. package/dist/components/ServiceItem/ServiceItemMobile.js +43 -17
  8. package/dist/components/ServiceItem/mobileTypes.d.ts +48 -2
  9. package/dist/components/ServiceItem/types.d.ts +27 -8
  10. package/dist/styles.css +219 -16
  11. package/dist/ui/ExpendedDropDown/ExpandedDropdown.d.ts +1 -2
  12. package/dist/ui/ExpendedDropDown/ExpandedDropdown.js +2 -4
  13. package/dist/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.js +3 -10
  14. package/dist/ui/OfferBanner.d.ts +2 -0
  15. package/dist/ui/OfferBanner.js +22 -15
  16. package/dist/ui/SeatSection/SeatSection.d.ts +1 -7
  17. package/dist/ui/SeatSection/SeatSection.js +12 -41
  18. package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +1 -2
  19. package/dist/ui/mobileweb/DateTimeSectionMobile.js +6 -12
  20. package/dist/ui/mobileweb/SeatSectionMobile.d.ts +1 -2
  21. package/dist/ui/mobileweb/SeatSectionMobile.js +14 -21
  22. package/dist/utils/CommonService.d.ts +4 -1
  23. package/dist/utils/CommonService.js +19 -6
  24. package/package.json +1 -1
  25. package/src/KuposUIComponent.tsx +3 -0
  26. package/src/assets/images/anims/service_list/flame_anim.json +1 -0
  27. package/src/assets/images/anims/service_list/thunder_icon.json +1 -0
  28. package/src/assets/images/anims/service_list/users_anim.json +1 -0
  29. package/src/components/ServiceItem/PeruServiceItemDesktop.tsx +404 -277
  30. package/src/components/ServiceItem/ServiceItemDesktop.tsx +71 -51
  31. package/src/components/ServiceItem/ServiceItemMobile.tsx +387 -290
  32. package/src/components/ServiceItem/mobileTypes.ts +50 -8
  33. package/src/components/ServiceItem/types.ts +32 -13
  34. package/src/styles.css +15 -0
  35. package/src/ui/ExpendedDropDown/ExpandedDropdown.tsx +2 -4
  36. package/src/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.tsx +575 -0
  37. package/src/ui/FeatureServiceUI/FeatureServiceUi.tsx +634 -0
  38. package/src/ui/OfferBanner.tsx +71 -43
  39. package/src/ui/SeatSection/SeatSection.tsx +21 -86
  40. package/src/ui/mobileweb/DateTimeSectionMobile.tsx +35 -44
  41. package/src/ui/mobileweb/SeatSectionMobile.tsx +11 -23
  42. package/src/utils/CommonService.ts +27 -8
@@ -19,6 +19,8 @@ interface OfferBannerProps {
19
19
  viewersConfig: ServiceItemProps["viewersConfig"];
20
20
  getAnimationIcon: (name: string) => any;
21
21
  showLoginOption?: boolean;
22
+ isNewUiEnabled?: boolean;
23
+ colors: any;
22
24
  }
23
25
 
24
26
  const OfferBanner: React.FC<OfferBannerProps> = ({
@@ -31,12 +33,17 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
31
33
  viewersConfig,
32
34
  getAnimationIcon,
33
35
  showLoginOption,
36
+ isNewUiEnabled,
37
+ colors,
34
38
  }) => {
35
39
  return (
36
40
  <div
37
- className="text-white p-[10px_15px] text-left w-full flex items-center absolute -bottom-[36px] pt-[50px] rounded-b-[14px] text-[14px]"
41
+ className="text-white p-[15px_15px] text-left w-full flex items-center absolute -bottom-[44px] pt-[50px] rounded-b-[14px] text-[14px] mt-[10px]"
38
42
  style={{
39
- background: offerGradient,
43
+ background:
44
+ serviceItem?.offer_text && !isNewUiEnabled
45
+ ? colors?.bottomStripColor
46
+ : offerGradient,
40
47
  opacity: isSoldOut ? 0.5 : 1,
41
48
  // zIndex: 0,
42
49
  }}
@@ -56,7 +63,7 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
56
63
  {/* starAnimation */}
57
64
  <span>Servicio popular entre los usuarios</span>
58
65
  </div>
59
- ) : (
66
+ ) : isNewUiEnabled && serviceItem?.offer_text ? (
60
67
  <div className="flex items-center">
61
68
  <LottiePlayer
62
69
  animationData={getAnimationIcon("bombAnimation")}
@@ -73,18 +80,14 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
73
80
  {(serviceItem?.offer_text || "").length > 30
74
81
  ? (serviceItem?.offer_text || "").slice(0, 30) + "..."
75
82
  : serviceItem?.offer_text || ""}{" "}
76
- {isLoggedIn && showLoginOption ? null : Object.keys(
77
- serviceItem?.dp_discount_percents ?? {},
78
- ).length > 0 ||
79
- (serviceItem?.dp_discounted_seats ?? []).length >
80
- 0 ? null : (
83
+ {isLoggedIn && showLoginOption ? null : (
81
84
  <span onClick={showLoginModal} className="cursor-pointer">
82
85
  - registro
83
86
  </span>
84
87
  )}{" "}
85
88
  &nbsp;
86
89
  </span>{" "}
87
- {serviceItem?.offer_text ? "| " : ""}
90
+ {serviceItem?.offer_text ? "|" : ""}
88
91
  Termina en&nbsp;
89
92
  <span
90
93
  className="bold-text text-end"
@@ -96,45 +99,70 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
96
99
  />
97
100
  </div>
98
101
  </div>
102
+ ) : (
103
+ serviceItem?.offer_text &&
104
+ !isNewUiEnabled && (
105
+ <div className="flex items-center">
106
+ <LottiePlayer
107
+ animationData={getAnimationIcon("promoAnim")}
108
+ width="18px"
109
+ height="18px"
110
+ />
111
+ <div className="flex items-center mt-[2px]">
112
+ <span
113
+ className="bold-text"
114
+ style={{
115
+ marginLeft: serviceItem?.offer_text ? "6px" : "3px",
116
+ }}
117
+ >
118
+ {serviceItem?.offer_text || ""}
119
+ </span>{" "}
120
+ </div>
121
+ </div>
122
+ )
99
123
  )}
100
124
  </div>
101
- <div className="flex items-center">
102
- {/* {renderIcon("personIcon", "16px")} */}
103
- <LottiePlayer
104
- animationData={getAnimationIcon("dotAnimation")}
105
- width="12px"
106
- height="12px"
107
- />
125
+ {(isNewUiEnabled || serviceItem?.is_dp_enabled) && (
126
+ <div className="flex items-center">
127
+ {/* {renderIcon("personIcon", "16px")} */}
128
+ <LottiePlayer
129
+ animationData={getAnimationIcon("dotAnimation")}
130
+ width="12px"
131
+ height="12px"
132
+ />
108
133
 
109
- <span className="ml-[6px]">
110
- <span
111
- className="bold-text"
112
- ref={(node) =>
113
- CommonService.startViewerCount(node, viewersConfig)
114
- }
115
- style={{ fontVariantNumeric: "tabular-nums" }}
116
- />{" "}
117
- {/* <span className="bold-text">personas</span>{" "} */}
118
- <span>
119
- {" "}
120
- {viewersConfig?.label || " viendo"} |{" "}
121
- <span className="">
122
- {serviceItem?.is_dp_enabled &&
123
- Object.keys(serviceItem?.dp_discount_percents ?? {}).length ===
124
- 0 &&
125
- (serviceItem?.dp_discounted_seats ?? []).length === 0
126
- ? null
127
- : "Quedan pocos • "}
128
- <span
129
- className="bold-text"
130
- ref={(node) => CommonService.startComprandoCount(node, 4, 16)}
131
- style={{ fontVariantNumeric: "tabular-nums" }}
132
- />{" "}
133
- comprando
134
+ <span className="ml-[6px]">
135
+ <span
136
+ className="bold-text"
137
+ ref={(node) =>
138
+ CommonService.startViewerCount(node, viewersConfig)
139
+ }
140
+ style={{ fontVariantNumeric: "tabular-nums" }}
141
+ />{" "}
142
+ {/* <span className="bold-text">personas</span>{" "} */}
143
+ <span>
144
+ {" "}
145
+ {viewersConfig?.label || " viendo"} |{" "}
146
+ <span className="">
147
+ {serviceItem?.is_dp_enabled &&
148
+ Object.keys(serviceItem?.dp_discount_percents ?? {})
149
+ .length === 0 &&
150
+ (serviceItem?.dp_discounted_seats ?? []).length === 0
151
+ ? null
152
+ : "Quedan pocos • "}
153
+ <span
154
+ className="bold-text"
155
+ ref={(node) =>
156
+ CommonService.startComprandoCount(node, 4, 16)
157
+ }
158
+ style={{ fontVariantNumeric: "tabular-nums" }}
159
+ />{" "}
160
+ comprando
161
+ </span>
134
162
  </span>
135
163
  </span>
136
- </span>
137
- </div>
164
+ </div>
165
+ )}
138
166
  </div>
139
167
  </div>
140
168
  );
@@ -7,8 +7,6 @@ interface SeatType {
7
7
  label: string;
8
8
  fare: number;
9
9
  key: any;
10
- apiSeatType?: string;
11
- api_seat_type?: string;
12
10
  }
13
11
 
14
12
  interface SeatSectionProps {
@@ -23,15 +21,6 @@ interface SeatSectionProps {
23
21
  serviceItem?: any;
24
22
  renderIcon?: (iconKey: string, size?: string) => React.ReactNode;
25
23
  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;
35
24
  }
36
25
 
37
26
  function getAllSeatTypes(seatTypes: SeatType[]) {
@@ -42,8 +31,6 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
42
31
  let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
43
32
  label: val?.label,
44
33
  price: val?.fare,
45
- key: val?.key,
46
- apiSeatType: val?.apiSeatType || val?.api_seat_type,
47
34
  }));
48
35
 
49
36
  seatTypesWithPrices.sort((a, b) => a.price - b.price);
@@ -51,7 +38,7 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
51
38
  return seatTypesWithPrices;
52
39
  }
53
40
 
54
- function getSortedSeatTypes(seatTypes: SeatType[], isTrain: any) {
41
+ function getSortedSeatTypes(seatTypes: SeatType[]) {
55
42
  if (!seatTypes?.length) {
56
43
  return [{ label: "Salon cama", price: 0 }];
57
44
  }
@@ -65,9 +52,7 @@ function getSortedSeatTypes(seatTypes: SeatType[], isTrain: any) {
65
52
  seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
66
53
  }
67
54
 
68
- if (!isTrain) {
69
- seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
70
- }
55
+ seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
71
56
 
72
57
  const seenPrices = new Set<number>();
73
58
  seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
@@ -112,18 +97,14 @@ function SeatSection({
112
97
  priceColor,
113
98
  currencySign,
114
99
  removeDuplicateSeats,
115
- selectedSeatKey,
116
- onSeatSelect,
117
100
  isPeru,
118
101
  serviceItem,
119
102
  renderIcon,
120
103
  dpSeatColor,
121
104
  discountSeatPriceColor,
122
- isTrain,
123
- topLabelColor,
124
105
  }: SeatSectionProps): React.ReactElement {
125
106
  const uniqueSeats = getUniqueSeats(seatTypes);
126
- const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
107
+ const sortedSeatTypes = getSortedSeatTypes(seatTypes);
127
108
  const numberOfSeats = getNumberOfSeats(seatTypes);
128
109
  const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
129
110
 
@@ -135,68 +116,22 @@ function SeatSection({
135
116
  const renderSeatNames = () => {
136
117
  const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
137
118
 
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
- }
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
+ }`}
156
126
  >
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
- });
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
+ );
200
135
  };
201
136
 
202
137
  const renderSeatPrices = () => {
@@ -342,7 +277,7 @@ function SeatSection({
342
277
  {!isNaN(Number(dpDiscountPercent)) &&
343
278
  Number(dpDiscountPercent) > 0 && (
344
279
  <span
345
- className="rounded-[100px] bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white"
280
+ className={`rounded-[100px] ${discountSeatPriceColor} bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white`}
346
281
  style={{
347
282
  animation: "pulse-zoom 2s ease-in-out infinite",
348
283
  whiteSpace: "nowrap",
@@ -384,7 +319,7 @@ function SeatSection({
384
319
  >
385
320
  <div
386
321
  className="absolute"
387
- style={{ left: isPeru ? "-19px" : "-19px", bottom: "1px" }}
322
+ style={{ left: isPeru ? "-1px" : "-19px", bottom: "1px" }}
388
323
  >
389
324
  {renderIcon("fireIcon", "16px")}
390
325
  </div>
@@ -541,7 +476,7 @@ function SeatSection({
541
476
  <div
542
477
  className="absolute"
543
478
  style={{
544
- left: isPeru ? "-18px" : "-18px",
479
+ left: isPeru ? "-1px" : "-18px",
545
480
  bottom: "1px",
546
481
  }}
547
482
  >
@@ -25,7 +25,6 @@ interface DateTimeSectionMobileProps {
25
25
  tooltipBgColor?: string;
26
26
  showLastSeats?: boolean;
27
27
  discountSeatPriceColor?: string;
28
- isTrain?: boolean;
29
28
  }
30
29
 
31
30
  const pad = (n: number) => (n < 10 ? "0" + n : String(n));
@@ -68,43 +67,41 @@ const TimeRow: React.FC<TimeRowProps> = ({
68
67
  isSoldOut,
69
68
  }) => {
70
69
  const formattedDate = DateService.getServiceItemDate(date);
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>
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>
105
102
  </div>
106
103
  </div>
107
- );
104
+ </div>;
108
105
  };
109
106
 
110
107
  function DateTimeSectionMobile({
@@ -130,7 +127,6 @@ function DateTimeSectionMobile({
130
127
  tooltipBgColor,
131
128
  showLastSeats,
132
129
  discountSeatPriceColor,
133
- isTrain,
134
130
  }: DateTimeSectionMobileProps): React.ReactElement {
135
131
  const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
136
132
 
@@ -157,12 +153,8 @@ function DateTimeSectionMobile({
157
153
  >
158
154
  {/* DATE AND TIME */}
159
155
  <div
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
- }}
156
+ className="min-h-[2.5rem] flex flex-col justify-between gap-[4px] w-[50%] "
157
+ style={{ justifyContent: isCiva && "center" }}
166
158
  >
167
159
  <TimeRow
168
160
  label={orignLabel}
@@ -206,7 +198,6 @@ function DateTimeSectionMobile({
206
198
  tooltipBgColor={tooltipBgColor}
207
199
  showLastSeats={showLastSeats}
208
200
  discountSeatPriceColor={discountSeatPriceColor}
209
- isTrain={isTrain}
210
201
  />
211
202
  </div>
212
203
  );
@@ -31,7 +31,6 @@ interface SeatSectionMobileProps {
31
31
  tooltipBgColor?: string;
32
32
  showLastSeats?: boolean;
33
33
  discountSeatPriceColor?: string;
34
- isTrain?: boolean;
35
34
  }
36
35
 
37
36
  interface SeatRowProps {
@@ -43,7 +42,6 @@ interface SeatRowProps {
43
42
  seatPriceColor: string;
44
43
  hasMultipleTypes: boolean;
45
44
  textSize: string;
46
- isTrain?: boolean;
47
45
  }
48
46
 
49
47
  const SeatRow: React.FC<SeatRowProps> = ({
@@ -55,7 +53,6 @@ const SeatRow: React.FC<SeatRowProps> = ({
55
53
  seatPriceColor,
56
54
  hasMultipleTypes,
57
55
  textSize,
58
- isTrain,
59
56
  }) => {
60
57
  if (EXCEPTIONS.includes(type.label)) return null;
61
58
 
@@ -72,9 +69,7 @@ const SeatRow: React.FC<SeatRowProps> = ({
72
69
  className={`min-[420]:text-[13px] ${textSize} `}
73
70
  style={{ color: labelColor }}
74
71
  >
75
- {isTrain
76
- ? commonService.truncateSeatLabel(displayLabel, 8)
77
- : displayLabel}
72
+ {displayLabel}
78
73
  </span>
79
74
  <span
80
75
  className={`min-[420]:text-[13px] ${textSize} bold-text`}
@@ -123,7 +118,6 @@ function SeatSectionMobile({
123
118
  tooltipBgColor,
124
119
  showLastSeats,
125
120
  discountSeatPriceColor,
126
- isTrain,
127
121
  }: SeatSectionMobileProps): React.ReactElement {
128
122
  const hasMultipleTypes = (seatTypesData?.length ?? 0) > 2;
129
123
 
@@ -284,17 +278,15 @@ function SeatSectionMobile({
284
278
  seatPriceColor={seatPriceColor}
285
279
  hasMultipleTypes={hasMultipleTypes}
286
280
  textSize="text-[11px]"
287
- isTrain={isTrain}
288
281
  />
289
282
  ));
290
283
  }
291
284
 
292
- const filteredSeats = seatTypesData
285
+ return seatTypesData
293
286
  ?.filter((item) => getFilteredSeats(item.label))
294
- ?.sort((a, b) => a.fare - b.fare);
295
-
296
- return (isTrain ? filteredSeats : filteredSeats?.slice(0, 2))?.map(
297
- (type, i) => (
287
+ ?.sort((a, b) => a.fare - b.fare)
288
+ ?.slice(0, 2)
289
+ ?.map((type, i) => (
298
290
  <SeatRow
299
291
  key={i}
300
292
  type={type}
@@ -305,20 +297,16 @@ function SeatSectionMobile({
305
297
  seatPriceColor={seatPriceColor}
306
298
  hasMultipleTypes={hasMultipleTypes}
307
299
  textSize="text-[12px]"
308
- isTrain={isTrain}
309
300
  />
310
- ),
311
- );
301
+ ));
312
302
  };
313
303
 
314
304
  const seats = removeDuplicateSeats
315
305
  ? getUniqueSeats(seatTypesData, 3)
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
- })();
306
+ : seatTypesData
307
+ ?.filter((item) => getFilteredSeats(item.label))
308
+ ?.sort((a, b) => a.fare - b.fare)
309
+ ?.slice(0, 2);
322
310
 
323
311
  const discountedSeats = seats?.map((seat) => ({
324
312
  ...seat,
@@ -553,7 +541,7 @@ function SeatSectionMobile({
553
541
  </div>
554
542
  ) : (
555
543
  <div
556
- className={`flex flex-col justify-between ${isTrain ? "" : "h-[2.5rem]"} `}
544
+ className="flex flex-col justify-between h-[2.5rem] "
557
545
  style={{
558
546
  gap: isSoldOut ? "0px" : "5px",
559
547
  justifyContent: hasMultipleTypes ? "space-between" : "center",
@@ -39,15 +39,9 @@ const commonService = {
39
39
  }
40
40
  },
41
41
 
42
- truncateSeatLabel: (label: string | number, maxLength?: number): string => {
42
+ truncateSeatLabel: (label: string | 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
-
51
45
  const words = label.trim().split(/\s+/);
52
46
 
53
47
  const truncateWord = (word: string) =>
@@ -296,6 +290,9 @@ const commonService = {
296
290
  discount_type?: string;
297
291
  discount_value?: number;
298
292
  max_discount?: number;
293
+ discounts?: Array<{
294
+ new_ui_enabled?: boolean;
295
+ }>;
299
296
  },
300
297
  ): { originalPrice: number; discountedPrice: number } => {
301
298
  const price =
@@ -308,6 +305,16 @@ const commonService = {
308
305
  }
309
306
 
310
307
  const { discount_type, discount_value, max_discount } = serviceItem;
308
+ // Check if there's a discount with new_ui_enabled = true
309
+ const newUiEnabled = serviceItem.discounts?.some(
310
+ (d) => d.new_ui_enabled === true
311
+ );
312
+
313
+
314
+ // Only apply discount if new_ui_enabled is true OR if regular discount exists
315
+ if (!newUiEnabled && (!discount_type || discount_value == null)) {
316
+ return { originalPrice: price, discountedPrice: price };
317
+ }
311
318
 
312
319
  const fixedDiscount =
313
320
  discount_type === "fixed" && discount_value != null ? discount_value : 0;
@@ -335,10 +342,15 @@ const commonService = {
335
342
  ) => {
336
343
  if (!node || !viewersConfig) return;
337
344
 
345
+ const { min, max, interval = 5000 } = viewersConfig;
346
+ const configKey = `${min}-${max}-${interval}`;
347
+ if (node.dataset.viewerId && node.dataset.viewerConfig === configKey) {
348
+ return;
349
+ }
350
+
338
351
  const prevId = node.dataset.viewerId;
339
352
  if (prevId) clearInterval(Number(prevId));
340
353
 
341
- const { min, max, interval = 5000 } = viewersConfig;
342
354
  const clamp = (v: number) => Math.min(max, Math.max(min, v));
343
355
  const initialValue = Math.floor(Math.random() * (max - min + 1)) + min;
344
356
 
@@ -353,6 +365,7 @@ const commonService = {
353
365
  }, interval);
354
366
 
355
367
  node.dataset.viewerId = String(id);
368
+ node.dataset.viewerConfig = configKey;
356
369
  },
357
370
 
358
371
  startCountdown: (
@@ -394,6 +407,11 @@ const commonService = {
394
407
  ) => {
395
408
  if (!node) return;
396
409
 
410
+ const configKey = `${min}-${max}`;
411
+ if (node.dataset.comprandoId && node.dataset.comprandoConfig === configKey) {
412
+ return;
413
+ }
414
+
397
415
  const prevId = node.dataset.comprandoId;
398
416
  if (prevId) clearInterval(Number(prevId));
399
417
 
@@ -414,6 +432,7 @@ const commonService = {
414
432
  }, 5000); // Update every 5 seconds
415
433
 
416
434
  node.dataset.comprandoId = String(id);
435
+ node.dataset.comprandoConfig = configKey;
417
436
  },
418
437
  };
419
438