kupos-ui-components-lib 10.4.62 → 10.4.64

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 (39) hide show
  1. package/dist/components/FilterBar/FilterBarDesktop.js +10 -0
  2. package/dist/components/FilterBar/ServiceFilter.d.ts +1 -0
  3. package/dist/components/FilterBar/ServiceFilter.js +10 -4
  4. package/dist/components/ServiceItem/PeruServiceItemDesktop.js +3 -1
  5. package/dist/components/ServiceItem/PeruServiceItemMobile.js +5 -3
  6. package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
  7. package/dist/components/ServiceItem/ServiceItemDesktop.js +7 -5
  8. package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
  9. package/dist/components/ServiceItem/ServiceItemMobile.js +14 -16
  10. package/dist/components/ServiceItem/mobileTypes.d.ts +3 -0
  11. package/dist/components/ServiceItem/types.d.ts +3 -0
  12. package/dist/styles.css +3 -0
  13. package/dist/ui/OfferBanner.d.ts +1 -1
  14. package/dist/ui/OfferBanner.js +9 -5
  15. package/dist/ui/PeruOfferBanner.d.ts +1 -1
  16. package/dist/ui/PeruOfferBanner.js +5 -1
  17. package/dist/ui/RatingBlock.d.ts +1 -1
  18. package/dist/ui/RatingBlock.js +7 -3
  19. package/dist/ui/SeatSection/SeatSection.js +28 -13
  20. package/dist/ui/ServiceBadges/ServiceBadges.js +1 -1
  21. package/dist/ui/mobileweb/SeatSectionMobile.js +10 -5
  22. package/dist/utils/CommonService.d.ts +7 -0
  23. package/dist/utils/CommonService.js +6 -0
  24. package/package.json +1 -1
  25. package/src/components/FilterBar/FilterBarDesktop.tsx +16 -0
  26. package/src/components/FilterBar/ServiceFilter.tsx +12 -2
  27. package/src/components/ServiceItem/PeruServiceItemDesktop.tsx +4 -1
  28. package/src/components/ServiceItem/PeruServiceItemMobile.tsx +6 -3
  29. package/src/components/ServiceItem/ServiceItemDesktop.tsx +9 -3
  30. package/src/components/ServiceItem/ServiceItemMobile.tsx +25 -14
  31. package/src/components/ServiceItem/mobileTypes.ts +3 -0
  32. package/src/components/ServiceItem/types.ts +3 -0
  33. package/src/ui/OfferBanner.tsx +10 -5
  34. package/src/ui/PeruOfferBanner.tsx +7 -1
  35. package/src/ui/RatingBlock.tsx +7 -2
  36. package/src/ui/SeatSection/SeatSection.tsx +53 -29
  37. package/src/ui/ServiceBadges/ServiceBadges.tsx +1 -1
  38. package/src/ui/mobileweb/SeatSectionMobile.tsx +30 -20
  39. package/src/utils/CommonService.ts +13 -0
@@ -45,7 +45,8 @@ function addSeatDegree(label) {
45
45
  return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
46
46
  }
47
47
  function hasInclinationSeatTypes(seatTypes) {
48
- return (seatTypes !== null && seatTypes !== void 0 ? seatTypes : []).some((seat) => { var _a; return String((_a = seat === null || seat === void 0 ? void 0 : seat.label) !== null && _a !== void 0 ? _a : "").includes("°"); });
48
+ // Matches "160°" as well as a bare "160" label
49
+ return (seatTypes !== null && seatTypes !== void 0 ? seatTypes : []).some((seat) => { var _a; return /\b(120|140|160|180)\b/.test(String((_a = seat === null || seat === void 0 ? void 0 : seat.label) !== null && _a !== void 0 ? _a : "")); });
49
50
  }
50
51
  const getUniqueSeats = (data, limit) => {
51
52
  var _a;
@@ -116,7 +117,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
116
117
  marginRight: "4px",
117
118
  filter: isSoldOut ? "grayscale(1)" : "",
118
119
  } })),
119
- commonService.truncateSeatLabel(seat.label)),
120
+ commonService.truncateSeatLabel(commonService.addSeatDegree(seat.label))),
120
121
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(showOriginalFare ? originalPrice : discountedPrice, currencySign))));
121
122
  })));
122
123
  }
@@ -155,7 +156,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
155
156
  const { discountedPrice } = commonService.calculateDiscountedPrice(lowestFare, serviceItem);
156
157
  const priceColor = isSoldOut ? "#bbb" : tooltipBgColor;
157
158
  return (React.createElement("div", { className: "relative flex flex-col justify-center h-full", style: { opacity: isSoldOut ? 0.5 : 1 } },
158
- React.createElement("div", { className: "absolute right-[0px]", style: {
159
+ commonService.showDpBestPrice(serviceItem) && (React.createElement("div", { className: "absolute right-[0px]", style: {
159
160
  animation: "pulse-zoom 2s ease-in-out infinite",
160
161
  top: ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) < 10 &&
161
162
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) > 0) ||
@@ -165,7 +166,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
165
166
  } },
166
167
  React.createElement("span", { className: "rounded-[100px] px-[8px] text-[11px] bold-text leading-[20px] text-[#fff]", style: {
167
168
  backgroundColor: tooltipBgColor,
168
- } }, "Mejor precio")),
169
+ } }, "Mejor precio"))),
169
170
  React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
170
171
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px]" }, "Desde"),
171
172
  React.createElement("span", { className: "flex items-center gap-[4px] min-[420]:text-[13px] text-[14px] bold-text", style: { color: priceColor } },
@@ -254,12 +255,16 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
254
255
  const originalDpPrice = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.original_dp_price);
255
256
  const dpDiscountPercent = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents);
256
257
  const firstSeatFare = (_h = (_g = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label) && !EXCEPTIONS.includes(item.label))) === null || _g === void 0 ? void 0 : _g.sort((a, b) => a.fare - b.fare)[0]) === null || _h === void 0 ? void 0 : _h.fare;
257
- const hasDpDiscount = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) &&
258
+ // show_dp_ui: false falls through to the regular fare layout
259
+ const isDpUiHidden = commonService.isDpUiHidden(serviceItem);
260
+ const hasDpDiscount = !isDpUiHidden &&
261
+ (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) &&
258
262
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) &&
259
263
  originalDpPrice != null &&
260
264
  dpDiscountPercent != null &&
261
265
  firstSeatFare != null;
262
266
  return (React.createElement("div", { className: "content-center relative", style: { width: "40%", flexShrink: 0 } }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
267
+ !isDpUiHidden &&
263
268
  !((_j = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _j === void 0 ? void 0 : _j.length) &&
264
269
  !dpDiscountPercent ? (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem]", style: { gap: isSoldOut ? "0px" : "5px" } }, renderDpSeats())) : hasDpDiscount ? (
265
270
  // Antes/Desde rows spread top/bottom so they line up with the
@@ -16,6 +16,13 @@ declare const commonService: {
16
16
  formatDefaultLabel: (label: string) => string;
17
17
  addSeatDegree: (label: string) => string;
18
18
  getSeatDegreeIcon: (label: string, icons: any) => string | null;
19
+ isDpUiHidden: (serviceItem?: {
20
+ is_dp_enabled?: boolean;
21
+ show_dp_ui?: boolean;
22
+ }) => boolean;
23
+ showDpBestPrice: (serviceItem?: {
24
+ show_best_price?: boolean;
25
+ }) => boolean;
19
26
  calculateDiscountedPrice: (originalPrice: number | string, serviceItem?: {
20
27
  discount_type?: string;
21
28
  discount_value?: number;
@@ -332,6 +332,12 @@ const commonService = {
332
332
  const match = String(label !== null && label !== void 0 ? label : "").match(/\b(120|140|160|180)\b/);
333
333
  return match ? (icons === null || icons === void 0 ? void 0 : icons[`seat${match[1]}`]) || null : null;
334
334
  },
335
+ // The backend can switch off the DP look (gradient card, ribbon, DP fare
336
+ // block) per DP service with show_dp_ui: false. DP fare rules (no changes /
337
+ // cancellations) still apply. A missing flag keeps the current UI.
338
+ isDpUiHidden: (serviceItem) => Boolean(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.show_dp_ui) === false,
339
+ // "Mejor precio" pill on DP fares; a missing flag keeps the current UI.
340
+ showDpBestPrice: (serviceItem) => (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.show_best_price) !== false,
335
341
  calculateDiscountedPrice: (originalPrice, serviceItem) => {
336
342
  var _a;
337
343
  const price = typeof originalPrice === "string"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "10.4.62",
3
+ "version": "10.4.64",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -221,6 +221,22 @@ const FilterBarDesktop = ({
221
221
  });
222
222
  }
223
223
 
224
+ if (fil.type == "service_type") {
225
+ const activeOptions = fil.options.filter((it) => it.active);
226
+ if (activeOptions.length) {
227
+ filteredRoutes = filteredRoutes.filter((it) =>
228
+ activeOptions.some(
229
+ (f) =>
230
+ f.value ===
231
+ String(it.service_type || "")
232
+ .trim()
233
+ .replace(/\s+/g, " ")
234
+ .toLowerCase(),
235
+ ),
236
+ );
237
+ }
238
+ }
239
+
224
240
  if (fil.type == "operator") {
225
241
  let activeOptions = fil.options.filter((it) => it.active);
226
242
  filteredRoutes = filteredRoutes.filter((it) => {
@@ -23,6 +23,7 @@ interface ServiceFilterBarDesktopState {
23
23
  showTrainType: boolean;
24
24
  addMargin: boolean;
25
25
  showAmenities: boolean;
26
+ showServiceType: boolean;
26
27
  sortedFilters: any[];
27
28
  }
28
29
 
@@ -43,6 +44,7 @@ class ServiceFilter extends React.Component<
43
44
  showTrainType: props.initialOpenFilters?.includes("train_type") || true,
44
45
  addMargin: false,
45
46
  showAmenities: props.initialOpenFilters?.includes("amenities") || true,
47
+ showServiceType: true,
46
48
  sortedFilters: this.sortFilters(props.filtersArray),
47
49
  };
48
50
 
@@ -80,6 +82,7 @@ class ServiceFilter extends React.Component<
80
82
  const FILTER_ORDER = [
81
83
  "time",
82
84
  "operator",
85
+ "service_type",
83
86
  "special_departure",
84
87
  "tipo",
85
88
  "seat_types",
@@ -173,6 +176,10 @@ class ServiceFilter extends React.Component<
173
176
  type === "amenities"
174
177
  ? !prevState.showAmenities
175
178
  : prevState.showAmenities,
179
+ showServiceType:
180
+ type === "service_type"
181
+ ? !prevState.showServiceType
182
+ : prevState.showServiceType,
176
183
  };
177
184
  });
178
185
  }
@@ -186,6 +193,7 @@ class ServiceFilter extends React.Component<
186
193
  showSpecialDeparture,
187
194
  showTrainType,
188
195
  showAmenities,
196
+ showServiceType,
189
197
  } = this.state;
190
198
 
191
199
  const isOpen =
@@ -195,7 +203,8 @@ class ServiceFilter extends React.Component<
195
203
  (option.type === "tipo" && showTypeOptions) ||
196
204
  (option.type === "special_departure" && showSpecialDeparture) ||
197
205
  (option.type === "train_type" && showTrainType) ||
198
- (option.type === "amenities" && showAmenities);
206
+ (option.type === "amenities" && showAmenities) ||
207
+ (option.type === "service_type" && showServiceType);
199
208
 
200
209
  if (option?.type === "train_type" && !this.props.isTrain) {
201
210
  return null;
@@ -249,7 +258,8 @@ class ServiceFilter extends React.Component<
249
258
  option.type !== "seat_types" &&
250
259
  option.type !== "tipo" &&
251
260
  option.type !== "special_departure" &&
252
- option.type !== "train_type")) &&
261
+ option.type !== "train_type" &&
262
+ option.type !== "service_type")) &&
253
263
  option.options.map((val: any, i: number) => {
254
264
  let iconKey = "";
255
265
  if (val.icon === "sunrise") {
@@ -180,7 +180,10 @@ function PeruServiceItemDesktop({
180
180
  })();
181
181
 
182
182
  let isSoldOut = serviceItem.available_seats <= 0;
183
- const hasDpEnabled = serviceItem?.is_dp_enabled === true;
183
+ // show_dp_ui: false renders a DP service with the regular card UI
184
+ const hasDpEnabled =
185
+ serviceItem?.is_dp_enabled === true &&
186
+ !CommonService.isDpUiHidden(serviceItem);
184
187
  const peruDiscount = (serviceItem as any)?.discounts?.[0];
185
188
  const uiType = peruDiscount?.ui_type;
186
189
 
@@ -100,7 +100,10 @@ function PeruServiceItemMobile({
100
100
  offerText.length > OFFER_TEXT_MAX_CHARS
101
101
  ? `${offerText.slice(0, OFFER_TEXT_MAX_CHARS).trimEnd()}...`
102
102
  : offerText;
103
- const hasDpEnabled = serviceItem?.is_dp_enabled === true;
103
+ // show_dp_ui: false renders a DP service with the regular card UI
104
+ const hasDpEnabled =
105
+ serviceItem?.is_dp_enabled === true &&
106
+ !commonService.isDpUiHidden(serviceItem);
104
107
  const hasDpRibbon = hasDpEnabled && Boolean(serviceItem?.dp_discount_percents);
105
108
 
106
109
  // ui_type comes from the active discount entry: 1 = offer-text-only tier,
@@ -224,7 +227,7 @@ function PeruServiceItemMobile({
224
227
 
225
228
  return (
226
229
  <div
227
- className={`relative ${!hasOfferText || !serviceItem?.is_dp_enabled ? "mb-[14px]" : showTopLabel || serviceItem?.is_direct_trip ? "mb-[20px]" : "mb-[12px]"} ${
230
+ className={`relative ${!hasOfferText || !hasDpEnabled ? "mb-[14px]" : showTopLabel || serviceItem?.is_direct_trip ? "mb-[20px]" : "mb-[12px]"} ${
228
231
  serviceItem?.is_direct_trip ||
229
232
  isConexion ||
230
233
  serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
@@ -470,7 +473,7 @@ function PeruServiceItemMobile({
470
473
  style={{ lineHeight: 1.6 }}
471
474
  >
472
475
  <div className="flex justify-between items-center">
473
- {serviceItem?.is_dp_enabled &&
476
+ {hasDpEnabled &&
474
477
  Object.keys(serviceItem?.dp_discount_percents ?? {}).length ===
475
478
  0 &&
476
479
  (serviceItem?.dp_discounted_seats ?? []).length === 0 ? (
@@ -154,6 +154,7 @@ function ServiceItemPB({
154
154
  priority_stage_ids,
155
155
  isShowSeatIcon = false,
156
156
  showDesdeLabel = false,
157
+ showServiceTypeUnderLogo = false,
157
158
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
158
159
  const handleSeatSelect = (
159
160
  key: any,
@@ -277,11 +278,13 @@ function ServiceItemPB({
277
278
  return false;
278
279
  })();
279
280
 
281
+ // show_dp_ui: false renders a DP service with the regular card UI
282
+ const isDpUiHidden = CommonService.isDpUiHidden(serviceItem);
280
283
  const dpDiscountEntry = Object.entries(
281
284
  serviceItem?.dp_discount_percents || {},
282
285
  )[0];
283
- const dpDiscountPercent = dpDiscountEntry?.[1];
284
- const hasDpEnabled = serviceItem?.is_dp_enabled === true;
286
+ const dpDiscountPercent = isDpUiHidden ? undefined : dpDiscountEntry?.[1];
287
+ const hasDpEnabled = serviceItem?.is_dp_enabled === true && !isDpUiHidden;
285
288
 
286
289
  const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
287
290
  const offerGradientWithOpacity = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"}80 0%, ${colors.leftGradiantColor || "#ff8842"}80 100%)`;
@@ -669,6 +672,7 @@ function ServiceItemPB({
669
672
  translation={translation}
670
673
  isPeru={isPeru}
671
674
  isTrain={isTrain}
675
+ showServiceTypeUnderLogo={showServiceTypeUnderLogo}
672
676
  />
673
677
  </div>
674
678
 
@@ -778,7 +782,9 @@ function ServiceItemPB({
778
782
  isPeru={isPeru}
779
783
  isTrain={isTrain}
780
784
  isDpEnabled={serviceItem.is_dp_enabled}
781
- serviceType={serviceItem.service_type}
785
+ serviceType={
786
+ showServiceTypeUnderLogo ? undefined : serviceItem.service_type
787
+ }
782
788
  renderIcon={renderIcon}
783
789
  />
784
790
  </div>
@@ -69,6 +69,7 @@ function ServiceItemMobile({
69
69
  priority_stage_ids,
70
70
  isShowSeatIcon = false,
71
71
  showDesdeLabel = false,
72
+ showServiceTypeUnderLogo = false,
72
73
  }: MobileServiceItemProps): React.ReactElement {
73
74
  const isItemExpanded = serviceItem.id === isExpanded;
74
75
  const isPetSeat = (Object.keys(serviceItem?.pet_seat_info) || []).length > 0;
@@ -93,6 +94,9 @@ function ServiceItemMobile({
93
94
 
94
95
  const isLongOfferText = (serviceItem?.offer_text?.length || 0) > 35;
95
96
  const hasDpEnabled = serviceItem?.is_dp_enabled === true;
97
+ // show_dp_ui: false renders a DP service with the regular card UI
98
+ const showDpUi =
99
+ serviceItem?.is_dp_enabled && !commonService.isDpUiHidden(serviceItem);
96
100
 
97
101
  const isDiscountBelow20 =
98
102
  (serviceItem as any)?.discount_value > 0 &&
@@ -102,7 +106,7 @@ function ServiceItemMobile({
102
106
  const offerGradientWithOpacity = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"}80 0%, ${colors.leftGradiantColor || "#ff8842"}80 100%)`;
103
107
  const serviceCardStyle: React.CSSProperties =
104
108
  (isNewUiEnabled && serviceItem?.ui_type === 2) ||
105
- (serviceItem?.is_dp_enabled && serviceItem?.dp_discount_percents)
109
+ (showDpUi && serviceItem?.dp_discount_percents)
106
110
  ? {
107
111
  borderColor: isSoldOut ? "#ccc" : "transparent",
108
112
  borderStyle: "solid",
@@ -285,7 +289,7 @@ function ServiceItemMobile({
285
289
  />
286
290
  ) : (
287
291
  <div
288
- className={`relative ${!hasOfferText || !serviceItem?.is_dp_enabled ? "mb-[14px]" : showTopLabel || serviceItem?.is_direct_trip ? "mb-[20px]" : "mb-[12px]"} ${
292
+ className={`relative ${!hasOfferText || !showDpUi ? "mb-[14px]" : showTopLabel || serviceItem?.is_direct_trip ? "mb-[20px]" : "mb-[12px]"} ${
289
293
  serviceItem?.is_direct_trip ||
290
294
  isConexion ||
291
295
  serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
@@ -299,8 +303,7 @@ function ServiceItemMobile({
299
303
  <div
300
304
  className={`z-1 ${
301
305
  ((isNewUiEnabled && serviceItem?.ui_type === 2) ||
302
- (serviceItem?.is_dp_enabled &&
303
- serviceItem?.dp_discount_percents)) &&
306
+ (showDpUi && serviceItem?.dp_discount_percents)) &&
304
307
  !isSoldOut
305
308
  ? "rounded-[18px]"
306
309
  : "rounded-[10px] border border-[#ccc]"
@@ -340,6 +343,15 @@ function ServiceItemMobile({
340
343
  <div className="black-text min-[420]:text-[12px] text-[12px]">
341
344
  {serviceItem?.operator_details[2]}
342
345
  </div>
346
+ ) : showServiceTypeUnderLogo && serviceItem?.service_type ? (
347
+ <div
348
+ className="min-[420]:text-[13px] text-[12px] text-ellipsis overflow-hidden whitespace-nowrap max-w-[150px]"
349
+ style={{ color: isSoldOut ? "#bbb" : "#464647" }}
350
+ >
351
+ {commonService.capitalizeFirstLetter(
352
+ serviceItem.service_type,
353
+ )}
354
+ </div>
343
355
  ) : showRating ? (
344
356
  <div className="flex min-[420]:text-[13px] text-[12px] h-[22px] items-center">
345
357
  <div className="flex items-center">
@@ -436,7 +448,9 @@ function ServiceItemMobile({
436
448
  hoursIcon={renderIcon("hours", "14px")}
437
449
  duration={serviceItem.duration?.toString()}
438
450
  isDirectTrip={serviceItem?.is_direct_trip}
439
- serviceType={serviceItem?.service_type}
451
+ serviceType={
452
+ showServiceTypeUnderLogo ? undefined : serviceItem?.service_type
453
+ }
440
454
  directoColor={colors.directoColor}
441
455
  directoAnim={serviceItem.icons.directoAnim}
442
456
  isChangeTicket={serviceItem.is_change_ticket}
@@ -480,7 +494,7 @@ function ServiceItemMobile({
480
494
  isNewUiEnabled &&
481
495
  serviceItem?.ui_type === 1 &&
482
496
  !isSoldOut &&
483
- !serviceItem?.is_dp_enabled && (
497
+ !showDpUi && (
484
498
  <div
485
499
  className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
486
500
  style={{
@@ -523,8 +537,7 @@ function ServiceItemMobile({
523
537
 
524
538
  {/* 🔹 EXPANDABLE DROPDOWN (below the card) */}
525
539
  {((isNewUiEnabled && serviceItem?.ui_type === 2) ||
526
- (serviceItem?.is_dp_enabled &&
527
- serviceItem?.dp_discount_percents)) &&
540
+ (showDpUi && serviceItem?.dp_discount_percents)) &&
528
541
  !isSoldOut && (
529
542
  <div
530
543
  className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
@@ -542,7 +555,7 @@ function ServiceItemMobile({
542
555
  style={{ lineHeight: 1.6 }}
543
556
  >
544
557
  <div className="flex justify-between items-center">
545
- {serviceItem?.is_dp_enabled &&
558
+ {showDpUi &&
546
559
  Object.keys(serviceItem?.dp_discount_percents ?? {})
547
560
  .length === 0 &&
548
561
  (serviceItem?.dp_discounted_seats ?? []).length === 0 ? (
@@ -650,8 +663,7 @@ function ServiceItemMobile({
650
663
  position: "relative",
651
664
  zIndex:
652
665
  hasOfferText ||
653
- (serviceItem?.is_dp_enabled &&
654
- serviceItem?.dp_discount_percents) ||
666
+ (showDpUi && serviceItem?.dp_discount_percents) ||
655
667
  (isNewUiEnabled && serviceItem?.ui_type === 2)
656
668
  ? -2
657
669
  : -1,
@@ -662,14 +674,13 @@ function ServiceItemMobile({
662
674
  overflow: "hidden",
663
675
  minHeight: 0,
664
676
  position:
665
- hasOfferText || isNewUiEnabled || serviceItem?.is_dp_enabled
677
+ hasOfferText || isNewUiEnabled || showDpUi
666
678
  ? "relative"
667
679
  : undefined,
668
680
  zIndex:
669
681
  serviceItem?.offer_text ||
670
682
  (isNewUiEnabled && serviceItem?.ui_type === 2) ||
671
- (serviceItem?.is_dp_enabled &&
672
- serviceItem?.dp_discount_percents)
683
+ (showDpUi && serviceItem?.dp_discount_percents)
673
684
  ? -3
674
685
  : undefined,
675
686
  marginTop: hasOfferText ? "-15px" : "-10px",
@@ -40,6 +40,8 @@ export interface MobileServiceItemProps {
40
40
  train_type_label?: string;
41
41
  ladies_booked_seats?: string;
42
42
  is_dp_enabled?: boolean;
43
+ show_dp_ui?: boolean;
44
+ show_best_price?: boolean;
43
45
  offer_text?: string;
44
46
  is_direct_trip?: boolean;
45
47
  is_train_type?: boolean;
@@ -267,4 +269,5 @@ export interface MobileServiceItemProps {
267
269
  isShowSeatIcon?: boolean;
268
270
  // shows a small "Desde" label above the price in the seat section
269
271
  showDesdeLabel?: boolean;
272
+ showServiceTypeUnderLogo?: boolean;
270
273
  }
@@ -43,6 +43,8 @@ export interface ServiceItemProps {
43
43
  offer_text?: string;
44
44
  is_direct_trip?: boolean;
45
45
  is_dp_enabled?: boolean;
46
+ show_dp_ui?: boolean;
47
+ show_best_price?: boolean;
46
48
  dp_discount_percents?: any;
47
49
  dp_discounted_seats?: any;
48
50
  original_dp_price?: any;
@@ -288,4 +290,5 @@ export interface ServiceItemProps {
288
290
  isShowSeatIcon?: boolean;
289
291
  // shows a small "Desde" label above the price in the seat section
290
292
  showDesdeLabel?: boolean;
293
+ showServiceTypeUnderLogo?: boolean;
291
294
  }
@@ -8,6 +8,7 @@ import { ServiceItemProps } from "../components/ServiceItem/types";
8
8
  type ServiceItemSlice = Pick<
9
9
  ServiceItemProps["serviceItem"],
10
10
  | "is_dp_enabled"
11
+ | "show_dp_ui"
11
12
  | "offer_text"
12
13
  | "dp_discount_percents"
13
14
  | "dp_discounted_seats"
@@ -49,6 +50,7 @@ function truncateOfferText(text: string): string {
49
50
  }
50
51
 
51
52
  function hasDpDiscounts(serviceItem: ServiceItemSlice): boolean {
53
+ if (CommonService.isDpUiHidden(serviceItem)) return false;
52
54
  return (
53
55
  Object.keys(serviceItem?.dp_discount_percents ?? {}).length > 0 ||
54
56
  (serviceItem?.dp_discounted_seats ?? []).length > 0
@@ -238,23 +240,26 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
238
240
  (serviceItem as any)?.discount_value > 0 &&
239
241
  (serviceItem as any)?.discount_value < 20;
240
242
  const offerText = serviceItem?.offer_text || "";
243
+ // With show_dp_ui: false the banner renders as for a non-DP service
244
+ const showDpUi =
245
+ serviceItem?.is_dp_enabled && !CommonService.isDpUiHidden(serviceItem);
241
246
 
242
247
  const isLegacyOffer =
243
248
  !!offerText &&
244
249
  isNewUiEnabled &&
245
250
  serviceItem?.ui_type === 1 &&
246
- !serviceItem?.is_dp_enabled;
251
+ !showDpUi;
247
252
 
248
253
  const showViewers =
249
254
  (isNewUiEnabled && serviceItem?.ui_type !== 1) ||
250
- serviceItem?.is_dp_enabled;
255
+ showDpUi;
251
256
 
252
257
  const background = isLegacyOffer ? colors?.bottomStripColor : offerGradient;
253
258
 
254
259
  const renderLeftContent = () => {
255
260
  const hasDp = hasDpDiscounts(serviceItem);
256
261
 
257
- if (serviceItem?.is_dp_enabled && !hasDp) {
262
+ if (showDpUi && !hasDp) {
258
263
  return <PopularServiceBanner getAnimationIcon={getAnimationIcon} />;
259
264
  }
260
265
 
@@ -292,14 +297,14 @@ const OfferBanner: React.FC<OfferBannerProps> = ({
292
297
  style={{
293
298
  background:
294
299
  (isNewUiEnabled && serviceItem?.ui_type === 2) ||
295
- (serviceItem?.is_dp_enabled && serviceItem?.dp_discount_percents)
300
+ (showDpUi && serviceItem?.dp_discount_percents)
296
301
  ? offerGradient
297
302
  : background,
298
303
  opacity: isSoldOut ? 0.5 : 1,
299
304
  zIndex: isLinatal ? "-1" : "0",
300
305
  paddingBottom:
301
306
  serviceItem?.offer_text && serviceItem?.ui_type !== 2
302
- ? "12px"
307
+ ? "7px"
303
308
  : "9px",
304
309
  }}
305
310
  >
@@ -8,6 +8,7 @@ import { ServiceItemProps } from "../components/ServiceItem/types";
8
8
  type ServiceItemSlice = Pick<
9
9
  ServiceItemProps["serviceItem"],
10
10
  | "is_dp_enabled"
11
+ | "show_dp_ui"
11
12
  | "offer_text"
12
13
  | "dp_discount_percents"
13
14
  | "dp_discounted_seats"
@@ -49,6 +50,7 @@ function truncateOfferText(text: string): string {
49
50
  }
50
51
 
51
52
  function hasDpDiscounts(serviceItem: ServiceItemSlice): boolean {
53
+ if (CommonService.isDpUiHidden(serviceItem)) return false;
52
54
  return (
53
55
  Object.keys(serviceItem?.dp_discount_percents ?? {}).length > 0 ||
54
56
  (serviceItem?.dp_discounted_seats ?? []).length > 0
@@ -232,7 +234,11 @@ const PeruOfferBanner: React.FC<PeruOfferBannerProps> = ({
232
234
  : offerGradient;
233
235
 
234
236
  const renderLeftContent = () => {
235
- if (serviceItem?.is_dp_enabled && !hasDp) {
237
+ if (
238
+ serviceItem?.is_dp_enabled &&
239
+ !CommonService.isDpUiHidden(serviceItem) &&
240
+ !hasDp
241
+ ) {
236
242
  return <PopularServiceBanner getAnimationIcon={getAnimationIcon} />;
237
243
  }
238
244
 
@@ -11,7 +11,12 @@ const RatingBlock = ({
11
11
  translation,
12
12
  isPeru = false,
13
13
  isTrain = false,
14
+ showServiceTypeUnderLogo = false,
14
15
  }: any) => {
16
+ const operatorLabel = showServiceTypeUnderLogo
17
+ ? commonService.capitalizeFirstLetter(serviceItem?.service_type)
18
+ : serviceItem.operator_details[2];
19
+
15
20
  return (
16
21
  <div
17
22
  className="flex items-center whitespace-nowrap"
@@ -38,7 +43,7 @@ const RatingBlock = ({
38
43
  <span
39
44
  className={`block max-w-[120px] cursor-pointer text-[12px] ${isPeru || isTrain ? "overflow-hidden text-ellipsis whitespace-nowrap" : ""}`}
40
45
  >
41
- {serviceItem.operator_details[2]}
46
+ {operatorLabel}
42
47
  </span>
43
48
  <div
44
49
  className="hidden group-hover:block absolute top-[24px] left-1/2 -translate-x-1/2 text-white p-3 rounded-[14px] whitespace-nowrap z-10 mt-2.5 w-max text-center shadow-service text-[12px]"
@@ -48,7 +53,7 @@ const RatingBlock = ({
48
53
  className="tooltip-arrow absolute -top-[7px] left-1/2 -translate-x-1/2 w-0 h-0 border-l-8 border-r-8 border-b-8 border-l-transparent border-r-transparent "
49
54
  style={{ borderBottomColor: colors.bottomStripColor }}
50
55
  ></div>
51
- {serviceItem.operator_details[2]}
56
+ {operatorLabel}
52
57
  </div>
53
58
  </div>
54
59
  </div>