kupos-ui-components-lib 10.4.63 → 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.
@@ -110,7 +110,9 @@ function PeruServiceItemDesktop({ serviceItem, onBookButtonPress, colors, metaDa
110
110
  return label;
111
111
  })();
112
112
  let isSoldOut = serviceItem.available_seats <= 0;
113
- const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true;
113
+ // show_dp_ui: false renders a DP service with the regular card UI
114
+ const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true &&
115
+ !CommonService.isDpUiHidden(serviceItem);
114
116
  const peruDiscount = (_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discounts) === null || _a === void 0 ? void 0 : _a[0];
115
117
  const uiType = peruDiscount === null || peruDiscount === void 0 ? void 0 : peruDiscount.ui_type;
116
118
  const renderIcon = (iconKey, size = "14px") => {
@@ -33,7 +33,9 @@ function PeruServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStag
33
33
  const displayOfferText = offerText.length > OFFER_TEXT_MAX_CHARS
34
34
  ? `${offerText.slice(0, OFFER_TEXT_MAX_CHARS).trimEnd()}...`
35
35
  : offerText;
36
- const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true;
36
+ // show_dp_ui: false renders a DP service with the regular card UI
37
+ const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true &&
38
+ !commonService.isDpUiHidden(serviceItem);
37
39
  const hasDpRibbon = hasDpEnabled && Boolean(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents);
38
40
  // ui_type comes from the active discount entry: 1 = offer-text-only tier,
39
41
  // 2 = full new-UI tier (timer, bomb animation, viewers).
@@ -117,7 +119,7 @@ function PeruServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStag
117
119
  const dropoffName = ((_f = serviceItem.stage_details_arr) === null || _f === void 0 ? void 0 : _f.length)
118
120
  ? extractStage(serviceItem.stage_details_arr, 1)
119
121
  : null;
120
- return (React.createElement("div", { className: `relative ${!hasOfferText || !(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) ? "mb-[14px]" : showTopLabel || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ? "mb-[20px]" : "mb-[12px]"} ${(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ||
122
+ return (React.createElement("div", { className: `relative ${!hasOfferText || !hasDpEnabled ? "mb-[14px]" : showTopLabel || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ? "mb-[20px]" : "mb-[12px]"} ${(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ||
121
123
  isConexion ||
122
124
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.train_type_label) === "Tren Express (Nuevo)" ||
123
125
  showTopLabel ||
@@ -185,7 +187,7 @@ function PeruServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStag
185
187
  } },
186
188
  React.createElement("div", { className: "flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]", style: { lineHeight: 1.6 } },
187
189
  React.createElement("div", { className: "flex justify-between items-center" },
188
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
190
+ hasDpEnabled &&
189
191
  Object.keys((_s = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) !== null && _s !== void 0 ? _s : {}).length ===
190
192
  0 &&
191
193
  ((_t = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) !== null && _t !== void 0 ? _t : []).length === 0 ? (React.createElement("div", { className: "flex items-center gap-[6px]" },
@@ -165,9 +165,11 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
165
165
  }
166
166
  return false;
167
167
  })();
168
+ // show_dp_ui: false renders a DP service with the regular card UI
169
+ const isDpUiHidden = CommonService.isDpUiHidden(serviceItem);
168
170
  const dpDiscountEntry = Object.entries((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) || {})[0];
169
- const dpDiscountPercent = dpDiscountEntry === null || dpDiscountEntry === void 0 ? void 0 : dpDiscountEntry[1];
170
- const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true;
171
+ const dpDiscountPercent = isDpUiHidden ? undefined : dpDiscountEntry === null || dpDiscountEntry === void 0 ? void 0 : dpDiscountEntry[1];
172
+ const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true && !isDpUiHidden;
171
173
  const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
172
174
  const offerGradientWithOpacity = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"}80 0%, ${colors.leftGradiantColor || "#ff8842"}80 100%)`;
173
175
  const serviceCardStyle = ((isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 2) ||
@@ -32,13 +32,15 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
32
32
  const hasDiscount = discountedSeats.some((seat) => seat.originalPrice !== seat.discountedPrice);
33
33
  const isLongOfferText = (((_d = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) === null || _d === void 0 ? void 0 : _d.length) || 0) > 35;
34
34
  const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true;
35
+ // show_dp_ui: false renders a DP service with the regular card UI
36
+ const showDpUi = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && !commonService.isDpUiHidden(serviceItem);
35
37
  const isDiscountBelow20 = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) > 0 &&
36
38
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) < 20;
37
39
  const hasOfferText = Boolean(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text);
38
40
  const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
39
41
  const offerGradientWithOpacity = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"}80 0%, ${colors.leftGradiantColor || "#ff8842"}80 100%)`;
40
42
  const serviceCardStyle = (isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 2) ||
41
- ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))
43
+ (showDpUi && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))
42
44
  ? {
43
45
  borderColor: isSoldOut ? "#ccc" : "transparent",
44
46
  borderStyle: "solid",
@@ -120,7 +122,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
120
122
  setIsFeatureDropDownExpand(isFeatureDropDownExpand === serviceItem.id ||
121
123
  isFeatureDropDownExpand === true
122
124
  ? null
123
- : serviceItem.id), selectedTimeSlot: selectedTimeSlot, onTimeSlotChange: onTimeSlotChange, isTimeDropdownOpen: isTimeDropdownOpen, onTimeDropdownToggle: onTimeDropdownToggle, wowDealData: wowDealData })) : (React.createElement("div", { className: `relative ${!hasOfferText || !(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) ? "mb-[14px]" : showTopLabel || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ? "mb-[20px]" : "mb-[12px]"} ${(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ||
125
+ : serviceItem.id), selectedTimeSlot: selectedTimeSlot, onTimeSlotChange: onTimeSlotChange, isTimeDropdownOpen: isTimeDropdownOpen, onTimeDropdownToggle: onTimeDropdownToggle, wowDealData: wowDealData })) : (React.createElement("div", { className: `relative ${!hasOfferText || !showDpUi ? "mb-[14px]" : showTopLabel || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ? "mb-[20px]" : "mb-[12px]"} ${(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_direct_trip) ||
124
126
  isConexion ||
125
127
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.train_type_label) === "Tren Express (Nuevo)" ||
126
128
  showTopLabel ||
@@ -128,8 +130,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
128
130
  ? "mt-[20px]"
129
131
  : "mt-[10px]"} `, style: { zIndex: 1 } },
130
132
  React.createElement("div", { className: `z-1 ${((isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 2) ||
131
- ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
132
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))) &&
133
+ (showDpUi && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))) &&
133
134
  !isSoldOut
134
135
  ? "rounded-[18px]"
135
136
  : "rounded-[10px] border border-[#ccc]"}`, style: Object.assign(Object.assign({}, serviceCardStyle), { backgroundColor: "#fff" }) },
@@ -169,7 +170,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
169
170
  isNewUiEnabled &&
170
171
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 1 &&
171
172
  !isSoldOut &&
172
- !(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]", style: {
173
+ !showDpUi && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]", style: {
173
174
  background: colors === null || colors === void 0 ? void 0 : colors.bottomStripColor,
174
175
  borderRadius: "0 0 14px 14px",
175
176
  zIndex: -1,
@@ -185,8 +186,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
185
186
  } },
186
187
  React.createElement("span", { className: "whitespace-nowrap min-[380px]:text-[12px]" }, serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text))))))),
187
188
  ((isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 2) ||
188
- ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
189
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))) &&
189
+ (showDpUi && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))) &&
190
190
  !isSoldOut && (React.createElement("div", { className: "px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]", style: {
191
191
  background: isSoldOut
192
192
  ? offerGradientWithOpacity
@@ -197,7 +197,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
197
197
  } },
198
198
  React.createElement("div", { className: "flex flex-col gap-[8px] text-[12px] min-[420px]:text-[12px] text-[#464647]", style: { lineHeight: 1.6 } },
199
199
  React.createElement("div", { className: "flex justify-between items-center" },
200
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
200
+ showDpUi &&
201
201
  Object.keys((_k = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) !== null && _k !== void 0 ? _k : {})
202
202
  .length === 0 &&
203
203
  ((_l = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) !== null && _l !== void 0 ? _l : []).length === 0 ? (React.createElement("div", { className: "flex items-center gap-[6px]" },
@@ -242,8 +242,7 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
242
242
  transition: "grid-template-rows 0.3s ease-in-out, opacity 0.25s ease-in-out",
243
243
  position: "relative",
244
244
  zIndex: hasOfferText ||
245
- ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
246
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents)) ||
245
+ (showDpUi && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents)) ||
247
246
  (isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 2)
248
247
  ? -2
249
248
  : -1,
@@ -251,13 +250,12 @@ function ServiceItemMobile({ serviceItem, onBookButtonPress, colors, busStage, o
251
250
  React.createElement("div", { style: {
252
251
  overflow: "hidden",
253
252
  minHeight: 0,
254
- position: hasOfferText || isNewUiEnabled || (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled)
253
+ position: hasOfferText || isNewUiEnabled || showDpUi
255
254
  ? "relative"
256
255
  : undefined,
257
256
  zIndex: (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) ||
258
257
  (isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 2) ||
259
- ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
260
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))
258
+ (showDpUi && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))
261
259
  ? -3
262
260
  : undefined,
263
261
  marginTop: hasOfferText ? "-15px" : "-10px",
@@ -37,6 +37,8 @@ export interface MobileServiceItemProps {
37
37
  train_type_label?: string;
38
38
  ladies_booked_seats?: string;
39
39
  is_dp_enabled?: boolean;
40
+ show_dp_ui?: boolean;
41
+ show_best_price?: boolean;
40
42
  offer_text?: string;
41
43
  is_direct_trip?: boolean;
42
44
  is_train_type?: boolean;
@@ -41,6 +41,8 @@ export interface ServiceItemProps {
41
41
  offer_text?: string;
42
42
  is_direct_trip?: boolean;
43
43
  is_dp_enabled?: boolean;
44
+ show_dp_ui?: boolean;
45
+ show_best_price?: boolean;
44
46
  dp_discount_percents?: any;
45
47
  dp_discounted_seats?: any;
46
48
  original_dp_price?: any;
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { ServiceItemProps } from "../components/ServiceItem/types";
3
- type ServiceItemSlice = Pick<ServiceItemProps["serviceItem"], "is_dp_enabled" | "offer_text" | "dp_discount_percents" | "dp_discounted_seats" | "show_register_button" | "ui_type">;
3
+ type ServiceItemSlice = Pick<ServiceItemProps["serviceItem"], "is_dp_enabled" | "show_dp_ui" | "offer_text" | "dp_discount_percents" | "dp_discounted_seats" | "show_register_button" | "ui_type">;
4
4
  interface OfferBannerColors {
5
5
  bottomStripColor?: string;
6
6
  }
@@ -10,6 +10,8 @@ function truncateOfferText(text) {
10
10
  }
11
11
  function hasDpDiscounts(serviceItem) {
12
12
  var _a, _b;
13
+ if (CommonService.isDpUiHidden(serviceItem))
14
+ return false;
13
15
  return (Object.keys((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) !== null && _a !== void 0 ? _a : {}).length > 0 ||
14
16
  ((_b = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) !== null && _b !== void 0 ? _b : []).length > 0);
15
17
  }
@@ -64,16 +66,18 @@ const OfferBanner = ({ offerGradient, isSoldOut, serviceItem, isLoggedIn, showLo
64
66
  const isDiscountBelow20 = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) > 0 &&
65
67
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) < 20;
66
68
  const offerText = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) || "";
69
+ // With show_dp_ui: false the banner renders as for a non-DP service
70
+ const showDpUi = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && !CommonService.isDpUiHidden(serviceItem);
67
71
  const isLegacyOffer = !!offerText &&
68
72
  isNewUiEnabled &&
69
73
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 1 &&
70
- !(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled);
74
+ !showDpUi;
71
75
  const showViewers = (isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) !== 1) ||
72
- (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled);
76
+ showDpUi;
73
77
  const background = isLegacyOffer ? colors === null || colors === void 0 ? void 0 : colors.bottomStripColor : offerGradient;
74
78
  const renderLeftContent = () => {
75
79
  const hasDp = hasDpDiscounts(serviceItem);
76
- if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && !hasDp) {
80
+ if (showDpUi && !hasDp) {
77
81
  return React.createElement(PopularServiceBanner, { getAnimationIcon: getAnimationIcon });
78
82
  }
79
83
  if (isLegacyOffer) {
@@ -86,13 +90,13 @@ const OfferBanner = ({ offerGradient, isSoldOut, serviceItem, isLoggedIn, showLo
86
90
  };
87
91
  return (React.createElement("div", { className: `text-white w-full absolute ${coachKey ? "-bottom-[40px]" : isExpand ? "-bottom-[40px]" : "-bottom-[40px]"} rounded-b-[14px] text-[14px] h-[58px] flex items-end `, style: {
88
92
  background: (isNewUiEnabled && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) === 2) ||
89
- ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))
93
+ (showDpUi && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents))
90
94
  ? offerGradient
91
95
  : background,
92
96
  opacity: isSoldOut ? 0.5 : 1,
93
97
  zIndex: isLinatal ? "-1" : "0",
94
98
  paddingBottom: (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.offer_text) && (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.ui_type) !== 2
95
- ? "12px"
99
+ ? "7px"
96
100
  : "9px",
97
101
  } },
98
102
  React.createElement("div", { className: "flex items-center w-full gap-[20px] px-[15px] justify-between" },
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { ServiceItemProps } from "../components/ServiceItem/types";
3
- type ServiceItemSlice = Pick<ServiceItemProps["serviceItem"], "is_dp_enabled" | "offer_text" | "dp_discount_percents" | "dp_discounted_seats" | "show_register_button">;
3
+ type ServiceItemSlice = Pick<ServiceItemProps["serviceItem"], "is_dp_enabled" | "show_dp_ui" | "offer_text" | "dp_discount_percents" | "dp_discounted_seats" | "show_register_button">;
4
4
  interface OfferBannerColors {
5
5
  bottomStripColor?: string;
6
6
  }
@@ -10,6 +10,8 @@ function truncateOfferText(text) {
10
10
  }
11
11
  function hasDpDiscounts(serviceItem) {
12
12
  var _a, _b;
13
+ if (CommonService.isDpUiHidden(serviceItem))
14
+ return false;
13
15
  return (Object.keys((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) !== null && _a !== void 0 ? _a : {}).length > 0 ||
14
16
  ((_b = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) !== null && _b !== void 0 ? _b : []).length > 0);
15
17
  }
@@ -64,7 +66,9 @@ const PeruOfferBanner = ({ offerGradient, isSoldOut, serviceItem, renderIcon, is
64
66
  ? (colors === null || colors === void 0 ? void 0 : colors.bottomStripColor) || offerGradient
65
67
  : offerGradient;
66
68
  const renderLeftContent = () => {
67
- if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && !hasDp) {
69
+ if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
70
+ !CommonService.isDpUiHidden(serviceItem) &&
71
+ !hasDp) {
68
72
  return React.createElement(PopularServiceBanner, { getAnimationIcon: getAnimationIcon });
69
73
  }
70
74
  if (isOfferTextOnly) {
@@ -281,6 +281,8 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
281
281
  // ).find(([, percent]) => Number(percent));
282
282
  const dpDiscountEntry = Object.entries((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) || {}).sort(([, a], [, b]) => Number(a) - Number(b))[0];
283
283
  const dpDiscountPercent = dpDiscountEntry === null || dpDiscountEntry === void 0 ? void 0 : dpDiscountEntry[1];
284
+ // show_dp_ui: false falls through to the regular fare layout
285
+ const isDpUiHidden = CommonService.isDpUiHidden(serviceItem);
284
286
  const getLowestDpValue = (source) => {
285
287
  if (!source)
286
288
  return undefined;
@@ -325,7 +327,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
325
327
  ? CommonService.currency(0, currencySign)
326
328
  : CommonService.discountedCurrency(Number(seatTypeFare), currencySign)))));
327
329
  };
328
- if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && !dpDiscountEntry) {
330
+ if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) && !isDpUiHidden && !dpDiscountEntry) {
329
331
  const dpSeats = (removeDuplicateSeats ? uniqueSeats : sortedSeatTypes).filter((s) => !SEAT_EXCEPTIONS.includes(s.label));
330
332
  const lowestFare = dpSeats.length > 0 ? Math.min(...dpSeats.map((s) => Number(s.price))) : 0;
331
333
  return (React.createElement("div", { className: "flex items-center justify-between text-[13.33px] ", style: {
@@ -337,7 +339,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
337
339
  React.createElement("div", null,
338
340
  React.createElement("div", { className: "relative" },
339
341
  React.createElement("div", { className: "absolute -left-[20px] transform -translate-y-1/2 top-[16px]" }, renderIcon("fireIcon", "16px")),
340
- React.createElement("div", { className: "absolute bottom-[35px] left-1/2 -translate-x-1/2 ", style: {
342
+ CommonService.showDpBestPrice(serviceItem) && (React.createElement("div", { className: "absolute bottom-[35px] left-1/2 -translate-x-1/2 ", style: {
341
343
  animation: "pulse-zoom 2s ease-in-out infinite",
342
344
  whiteSpace: "nowrap",
343
345
  } },
@@ -346,12 +348,12 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
346
348
  whiteSpace: "nowrap",
347
349
  color: dpSeatColor,
348
350
  backgroundColor: tooltipColor,
349
- } }, "Mejor precio")),
351
+ } }, "Mejor precio"))),
350
352
  React.createElement("div", { className: "text-[22px] bold-text leading-[30px] text-center", style: { color: isSoldOut ? "#c0c0c0" : dpSeatColor } }, availableSeats <= 0
351
353
  ? CommonService.currency(0, currencySign)
352
354
  : CommonService.discountedCurrency(lowestFare, currencySign))))));
353
355
  }
354
- if (dpDiscountEntry) {
356
+ if (dpDiscountEntry && !isDpUiHidden) {
355
357
  const originalDpPrice = getLowestDpValue(serviceItem.original_dp_price);
356
358
  const allFares = ((_a = serviceItem.seat_types) !== null && _a !== void 0 ? _a : [])
357
359
  .map((st) => st.fare)
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import CommonService from "../../utils/CommonService";
3
3
  const ServiceBadges = ({ showTopLabel, isSoldOut, colors, renderIcon, translation, serviceItem, isAllinBus = false, boardingName, getAnimationIcon, }) => {
4
- return (React.createElement("div", { className: "absolute -top-[13px] left-0 w-full flex items-center justify-end gap-[12px] pr-[22px] z-10" },
4
+ return (React.createElement("div", { className: "absolute -top-[11px] left-0 w-full flex items-center justify-end gap-[12px] pr-[22px] z-10" },
5
5
  showTopLabel && (React.createElement("div", { className: `flex items-center gap-[10px] py-[4px] px-[14px] rounded-[38px] text-[12.5px] z-10`, style: {
6
6
  backgroundColor: colors.topLabelBgColor || "#fff",
7
7
  border: isSoldOut
@@ -156,7 +156,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
156
156
  const { discountedPrice } = commonService.calculateDiscountedPrice(lowestFare, serviceItem);
157
157
  const priceColor = isSoldOut ? "#bbb" : tooltipBgColor;
158
158
  return (React.createElement("div", { className: "relative flex flex-col justify-center h-full", style: { opacity: isSoldOut ? 0.5 : 1 } },
159
- React.createElement("div", { className: "absolute right-[0px]", style: {
159
+ commonService.showDpBestPrice(serviceItem) && (React.createElement("div", { className: "absolute right-[0px]", style: {
160
160
  animation: "pulse-zoom 2s ease-in-out infinite",
161
161
  top: ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) < 10 &&
162
162
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.available_seats) > 0) ||
@@ -166,7 +166,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
166
166
  } },
167
167
  React.createElement("span", { className: "rounded-[100px] px-[8px] text-[11px] bold-text leading-[20px] text-[#fff]", style: {
168
168
  backgroundColor: tooltipBgColor,
169
- } }, "Mejor precio")),
169
+ } }, "Mejor precio"))),
170
170
  React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
171
171
  React.createElement("span", { className: "min-[420]:text-[13px] text-[12px]" }, "Desde"),
172
172
  React.createElement("span", { className: "flex items-center gap-[4px] min-[420]:text-[13px] text-[14px] bold-text", style: { color: priceColor } },
@@ -255,12 +255,16 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
255
255
  const originalDpPrice = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.original_dp_price);
256
256
  const dpDiscountPercent = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents);
257
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;
258
- 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) &&
259
262
  (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) &&
260
263
  originalDpPrice != null &&
261
264
  dpDiscountPercent != null &&
262
265
  firstSeatFare != null;
263
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 &&
264
268
  !((_j = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _j === void 0 ? void 0 : _j.length) &&
265
269
  !dpDiscountPercent ? (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem]", style: { gap: isSoldOut ? "0px" : "5px" } }, renderDpSeats())) : hasDpDiscount ? (
266
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.63",
3
+ "version": "10.4.64",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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 ? (
@@ -278,11 +278,13 @@ function ServiceItemPB({
278
278
  return false;
279
279
  })();
280
280
 
281
+ // show_dp_ui: false renders a DP service with the regular card UI
282
+ const isDpUiHidden = CommonService.isDpUiHidden(serviceItem);
281
283
  const dpDiscountEntry = Object.entries(
282
284
  serviceItem?.dp_discount_percents || {},
283
285
  )[0];
284
- const dpDiscountPercent = dpDiscountEntry?.[1];
285
- const hasDpEnabled = serviceItem?.is_dp_enabled === true;
286
+ const dpDiscountPercent = isDpUiHidden ? undefined : dpDiscountEntry?.[1];
287
+ const hasDpEnabled = serviceItem?.is_dp_enabled === true && !isDpUiHidden;
286
288
 
287
289
  const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
288
290
  const offerGradientWithOpacity = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"}80 0%, ${colors.leftGradiantColor || "#ff8842"}80 100%)`;
@@ -94,6 +94,9 @@ function ServiceItemMobile({
94
94
 
95
95
  const isLongOfferText = (serviceItem?.offer_text?.length || 0) > 35;
96
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);
97
100
 
98
101
  const isDiscountBelow20 =
99
102
  (serviceItem as any)?.discount_value > 0 &&
@@ -103,7 +106,7 @@ function ServiceItemMobile({
103
106
  const offerGradientWithOpacity = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"}80 0%, ${colors.leftGradiantColor || "#ff8842"}80 100%)`;
104
107
  const serviceCardStyle: React.CSSProperties =
105
108
  (isNewUiEnabled && serviceItem?.ui_type === 2) ||
106
- (serviceItem?.is_dp_enabled && serviceItem?.dp_discount_percents)
109
+ (showDpUi && serviceItem?.dp_discount_percents)
107
110
  ? {
108
111
  borderColor: isSoldOut ? "#ccc" : "transparent",
109
112
  borderStyle: "solid",
@@ -286,7 +289,7 @@ function ServiceItemMobile({
286
289
  />
287
290
  ) : (
288
291
  <div
289
- 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]"} ${
290
293
  serviceItem?.is_direct_trip ||
291
294
  isConexion ||
292
295
  serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
@@ -300,8 +303,7 @@ function ServiceItemMobile({
300
303
  <div
301
304
  className={`z-1 ${
302
305
  ((isNewUiEnabled && serviceItem?.ui_type === 2) ||
303
- (serviceItem?.is_dp_enabled &&
304
- serviceItem?.dp_discount_percents)) &&
306
+ (showDpUi && serviceItem?.dp_discount_percents)) &&
305
307
  !isSoldOut
306
308
  ? "rounded-[18px]"
307
309
  : "rounded-[10px] border border-[#ccc]"
@@ -492,7 +494,7 @@ function ServiceItemMobile({
492
494
  isNewUiEnabled &&
493
495
  serviceItem?.ui_type === 1 &&
494
496
  !isSoldOut &&
495
- !serviceItem?.is_dp_enabled && (
497
+ !showDpUi && (
496
498
  <div
497
499
  className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
498
500
  style={{
@@ -535,8 +537,7 @@ function ServiceItemMobile({
535
537
 
536
538
  {/* 🔹 EXPANDABLE DROPDOWN (below the card) */}
537
539
  {((isNewUiEnabled && serviceItem?.ui_type === 2) ||
538
- (serviceItem?.is_dp_enabled &&
539
- serviceItem?.dp_discount_percents)) &&
540
+ (showDpUi && serviceItem?.dp_discount_percents)) &&
540
541
  !isSoldOut && (
541
542
  <div
542
543
  className="px-[12px] pt-[22px] pb-[8px] relative -z-9 -mt-[15px]"
@@ -554,7 +555,7 @@ function ServiceItemMobile({
554
555
  style={{ lineHeight: 1.6 }}
555
556
  >
556
557
  <div className="flex justify-between items-center">
557
- {serviceItem?.is_dp_enabled &&
558
+ {showDpUi &&
558
559
  Object.keys(serviceItem?.dp_discount_percents ?? {})
559
560
  .length === 0 &&
560
561
  (serviceItem?.dp_discounted_seats ?? []).length === 0 ? (
@@ -662,8 +663,7 @@ function ServiceItemMobile({
662
663
  position: "relative",
663
664
  zIndex:
664
665
  hasOfferText ||
665
- (serviceItem?.is_dp_enabled &&
666
- serviceItem?.dp_discount_percents) ||
666
+ (showDpUi && serviceItem?.dp_discount_percents) ||
667
667
  (isNewUiEnabled && serviceItem?.ui_type === 2)
668
668
  ? -2
669
669
  : -1,
@@ -674,14 +674,13 @@ function ServiceItemMobile({
674
674
  overflow: "hidden",
675
675
  minHeight: 0,
676
676
  position:
677
- hasOfferText || isNewUiEnabled || serviceItem?.is_dp_enabled
677
+ hasOfferText || isNewUiEnabled || showDpUi
678
678
  ? "relative"
679
679
  : undefined,
680
680
  zIndex:
681
681
  serviceItem?.offer_text ||
682
682
  (isNewUiEnabled && serviceItem?.ui_type === 2) ||
683
- (serviceItem?.is_dp_enabled &&
684
- serviceItem?.dp_discount_percents)
683
+ (showDpUi && serviceItem?.dp_discount_percents)
685
684
  ? -3
686
685
  : undefined,
687
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;
@@ -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;
@@ -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
 
@@ -516,6 +516,8 @@ function SeatSection({
516
516
  serviceItem?.dp_discount_percents || {},
517
517
  ).sort(([, a], [, b]) => Number(a) - Number(b))[0];
518
518
  const dpDiscountPercent = dpDiscountEntry?.[1];
519
+ // show_dp_ui: false falls through to the regular fare layout
520
+ const isDpUiHidden = CommonService.isDpUiHidden(serviceItem);
519
521
  const getLowestDpValue = (source: any): number | undefined => {
520
522
  if (!source) return undefined;
521
523
  const values: any[] = Array.isArray(source)
@@ -611,7 +613,7 @@ function SeatSection({
611
613
  );
612
614
  };
613
615
 
614
- if (serviceItem?.is_dp_enabled && !dpDiscountEntry) {
616
+ if (serviceItem?.is_dp_enabled && !isDpUiHidden && !dpDiscountEntry) {
615
617
  const dpSeats = (
616
618
  removeDuplicateSeats ? uniqueSeats : sortedSeatTypes
617
619
  ).filter((s) => !SEAT_EXCEPTIONS.includes(s.label));
@@ -636,25 +638,27 @@ function SeatSection({
636
638
  <div className="absolute -left-[20px] transform -translate-y-1/2 top-[16px]">
637
639
  {renderIcon("fireIcon", "16px")}
638
640
  </div>
639
- <div
640
- className="absolute bottom-[35px] left-1/2 -translate-x-1/2 "
641
- style={{
642
- animation: "pulse-zoom 2s ease-in-out infinite",
643
- whiteSpace: "nowrap",
644
- }}
645
- >
646
- <span
647
- className="rounded-[100px] px-[6px] px-[2px] w-fit text-[12px] bold-text leading-[20px] text-white"
641
+ {CommonService.showDpBestPrice(serviceItem) && (
642
+ <div
643
+ className="absolute bottom-[35px] left-1/2 -translate-x-1/2 "
648
644
  style={{
649
645
  animation: "pulse-zoom 2s ease-in-out infinite",
650
646
  whiteSpace: "nowrap",
651
- color: dpSeatColor,
652
- backgroundColor: tooltipColor,
653
647
  }}
654
648
  >
655
- Mejor precio
656
- </span>
657
- </div>
649
+ <span
650
+ className="rounded-[100px] px-[6px] px-[2px] w-fit text-[12px] bold-text leading-[20px] text-white"
651
+ style={{
652
+ animation: "pulse-zoom 2s ease-in-out infinite",
653
+ whiteSpace: "nowrap",
654
+ color: dpSeatColor,
655
+ backgroundColor: tooltipColor,
656
+ }}
657
+ >
658
+ Mejor precio
659
+ </span>
660
+ </div>
661
+ )}
658
662
  <div
659
663
  className="text-[22px] bold-text leading-[30px] text-center"
660
664
  style={{ color: isSoldOut ? "#c0c0c0" : dpSeatColor }}
@@ -669,7 +673,7 @@ function SeatSection({
669
673
  );
670
674
  }
671
675
 
672
- if (dpDiscountEntry) {
676
+ if (dpDiscountEntry && !isDpUiHidden) {
673
677
  const originalDpPrice = getLowestDpValue(serviceItem.original_dp_price);
674
678
  const allFares = (serviceItem.seat_types ?? [])
675
679
  .map((st: any) => st.fare)
@@ -32,7 +32,7 @@ const ServiceBadges: React.FC<ServiceBadgesProps> = ({
32
32
  getAnimationIcon,
33
33
  }) => {
34
34
  return (
35
- <div className="absolute -top-[13px] left-0 w-full flex items-center justify-end gap-[12px] pr-[22px] z-10">
35
+ <div className="absolute -top-[11px] left-0 w-full flex items-center justify-end gap-[12px] pr-[22px] z-10">
36
36
  {showTopLabel && (
37
37
  <div
38
38
  className={`flex items-center gap-[10px] py-[4px] px-[14px] rounded-[38px] text-[12.5px] z-10`}
@@ -360,27 +360,29 @@ function SeatSectionMobile({
360
360
  className="relative flex flex-col justify-center h-full"
361
361
  style={{ opacity: isSoldOut ? 0.5 : 1 }}
362
362
  >
363
- <div
364
- className="absolute right-[0px]"
365
- style={{
366
- animation: "pulse-zoom 2s ease-in-out infinite",
367
- top:
368
- (serviceItem?.available_seats < 10 &&
369
- serviceItem?.available_seats > 0) ||
370
- isSoldOut
371
- ? "-20px"
372
- : "-15px",
373
- }}
374
- >
375
- <span
376
- className="rounded-[100px] px-[8px] text-[11px] bold-text leading-[20px] text-[#fff]"
363
+ {commonService.showDpBestPrice(serviceItem) && (
364
+ <div
365
+ className="absolute right-[0px]"
377
366
  style={{
378
- backgroundColor: tooltipBgColor,
367
+ animation: "pulse-zoom 2s ease-in-out infinite",
368
+ top:
369
+ (serviceItem?.available_seats < 10 &&
370
+ serviceItem?.available_seats > 0) ||
371
+ isSoldOut
372
+ ? "-20px"
373
+ : "-15px",
379
374
  }}
380
375
  >
381
- Mejor precio
382
- </span>
383
- </div>
376
+ <span
377
+ className="rounded-[100px] px-[8px] text-[11px] bold-text leading-[20px] text-[#fff]"
378
+ style={{
379
+ backgroundColor: tooltipBgColor,
380
+ }}
381
+ >
382
+ Mejor precio
383
+ </span>
384
+ </div>
385
+ )}
384
386
  <div className="w-[100%] flex flex-row justify-between items-center">
385
387
  <span
386
388
  className="min-[420]:text-[13px] text-[12px]"
@@ -568,7 +570,11 @@ function SeatSectionMobile({
568
570
  )
569
571
  ?.sort((a, b) => a.fare - b.fare)[0]?.fare;
570
572
 
573
+ // show_dp_ui: false falls through to the regular fare layout
574
+ const isDpUiHidden = commonService.isDpUiHidden(serviceItem);
575
+
571
576
  const hasDpDiscount =
577
+ !isDpUiHidden &&
572
578
  serviceItem?.dp_discounted_seats &&
573
579
  serviceItem?.dp_discount_percents &&
574
580
  originalDpPrice != null &&
@@ -581,6 +587,7 @@ function SeatSectionMobile({
581
587
  style={{ width: "40%", flexShrink: 0 }}
582
588
  >
583
589
  {serviceItem?.is_dp_enabled &&
590
+ !isDpUiHidden &&
584
591
  !serviceItem?.dp_discounted_seats?.length &&
585
592
  !dpDiscountPercent ? (
586
593
  <div
@@ -349,6 +349,19 @@ const commonService = {
349
349
  return match ? icons?.[`seat${match[1]}`] || null : null;
350
350
  },
351
351
 
352
+ // The backend can switch off the DP look (gradient card, ribbon, DP fare
353
+ // block) per DP service with show_dp_ui: false. DP fare rules (no changes /
354
+ // cancellations) still apply. A missing flag keeps the current UI.
355
+ isDpUiHidden: (serviceItem?: {
356
+ is_dp_enabled?: boolean;
357
+ show_dp_ui?: boolean;
358
+ }): boolean =>
359
+ Boolean(serviceItem?.is_dp_enabled) && serviceItem?.show_dp_ui === false,
360
+
361
+ // "Mejor precio" pill on DP fares; a missing flag keeps the current UI.
362
+ showDpBestPrice: (serviceItem?: { show_best_price?: boolean }): boolean =>
363
+ serviceItem?.show_best_price !== false,
364
+
352
365
  calculateDiscountedPrice: (
353
366
  originalPrice: number | string,
354
367
  serviceItem?: {