kupos-ui-components-lib 9.11.0 → 9.11.2
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.
- package/dist/components/ServiceItem/ServiceItemDesktop.js +20 -1
- package/dist/ui/SeatSection/SeatSection.js +34 -2
- package/dist/ui/mobileweb/SeatSectionMobile.js +28 -7
- package/package.json +1 -1
- package/src/components/ServiceItem/ServiceItemDesktop.tsx +23 -1
- package/src/ui/SeatSection/SeatSection.tsx +58 -2
- package/src/ui/mobileweb/SeatSectionMobile.tsx +58 -7
|
@@ -130,6 +130,24 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
|
|
|
130
130
|
: serviceItem.seat_types || [];
|
|
131
131
|
const discountedSeats = seats.map((seat) => (Object.assign(Object.assign({}, seat), CommonService.calculateDiscountedPrice(seat.fare, serviceItem))));
|
|
132
132
|
const hasDiscount = discountedSeats.some((seat) => seat.originalPrice !== seat.discountedPrice);
|
|
133
|
+
// Mirror the same check as SeatSection: hide badge (and its top margin) when
|
|
134
|
+
// both percentage and max_discount exist and the cap is being applied.
|
|
135
|
+
const isMaxDiscountApplied = (() => {
|
|
136
|
+
const { discount_type, discount_value, max_discount } = serviceItem;
|
|
137
|
+
if (discount_type === "percentage" &&
|
|
138
|
+
typeof discount_value === "number" &&
|
|
139
|
+
max_discount != null &&
|
|
140
|
+
max_discount > 0) {
|
|
141
|
+
const lowestFare = discountedSeats
|
|
142
|
+
.map((s) => s.originalPrice)
|
|
143
|
+
.filter((p) => p > 0)
|
|
144
|
+
.sort((a, b) => a - b)[0];
|
|
145
|
+
if (lowestFare != null) {
|
|
146
|
+
return (lowestFare * discount_value) / 100 > max_discount;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
})();
|
|
133
151
|
const dpDiscountEntry = Object.entries((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) || {})[0];
|
|
134
152
|
const dpDiscountPercent = dpDiscountEntry === null || dpDiscountEntry === void 0 ? void 0 : dpDiscountEntry[1];
|
|
135
153
|
const hasDpEnabled = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) === true;
|
|
@@ -277,7 +295,8 @@ function ServiceItemPB({ serviceItem, onBookButtonPress, colors, metaData, child
|
|
|
277
295
|
: coachKey
|
|
278
296
|
? "20px 15px 20px 15px"
|
|
279
297
|
: "20px 15px 10px 15px",
|
|
280
|
-
marginTop: hasDiscount || hasOfferText || dpDiscountPercent
|
|
298
|
+
marginTop: (hasDiscount || hasOfferText || dpDiscountPercent) &&
|
|
299
|
+
!isMaxDiscountApplied
|
|
281
300
|
? "14px"
|
|
282
301
|
: "",
|
|
283
302
|
} },
|
|
@@ -74,6 +74,17 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
|
|
|
74
74
|
};
|
|
75
75
|
const renderSeatPrices = () => {
|
|
76
76
|
if (isPeru) {
|
|
77
|
+
const isMovilBus = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_service_name) === "MovilBus";
|
|
78
|
+
// Multiple unique seat types → show a price row for each (MovilBus only)
|
|
79
|
+
if (isMovilBus && uniqueSeats.length > 1) {
|
|
80
|
+
return uniqueSeats
|
|
81
|
+
.filter((s) => !SEAT_EXCEPTIONS.includes(s.label))
|
|
82
|
+
.map((val, key) => {
|
|
83
|
+
const { discountedPrice } = CommonService.calculateDiscountedPrice(val.price, serviceItem);
|
|
84
|
+
return (React.createElement("span", { key: key, className: "flex items-center text-[13.33px] bold-text" }, formatPrice(discountedPrice)));
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Single seat type → original behaviour (one lowest-fare price)
|
|
77
88
|
const allSeats = getAllSeatTypes(seatTypes);
|
|
78
89
|
const lowestFare = allSeats.length > 0 ? allSeats[0].price : 0;
|
|
79
90
|
const { discountedPrice } = CommonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
@@ -115,8 +126,30 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
|
|
|
115
126
|
}
|
|
116
127
|
return null;
|
|
117
128
|
})();
|
|
129
|
+
// Hide the % OFF badge when max_discount is capping the percentage discount
|
|
130
|
+
// (i.e. both percentage and max_discount exist, and the raw % amount exceeds the cap)
|
|
131
|
+
const isMaxDiscountApplied = (() => {
|
|
132
|
+
const { discount_type, discount_value, max_discount } = serviceItem !== null && serviceItem !== void 0 ? serviceItem : {};
|
|
133
|
+
if (discount_type === "percentage" &&
|
|
134
|
+
typeof discount_value === "number" &&
|
|
135
|
+
max_discount != null &&
|
|
136
|
+
max_discount > 0 &&
|
|
137
|
+
discountSeat) {
|
|
138
|
+
const rawPercentageDiscount = (discountSeat.originalPrice * discount_value) / 100;
|
|
139
|
+
return rawPercentageDiscount > max_discount;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
})();
|
|
118
143
|
const renderLabels = () => {
|
|
119
144
|
if (isPeru) {
|
|
145
|
+
const isMovilBus = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_service_name) === "MovilBus";
|
|
146
|
+
// Multiple unique seat types → show a label row for each (MovilBus only)
|
|
147
|
+
if (isMovilBus && uniqueSeats.length > 1) {
|
|
148
|
+
return uniqueSeats
|
|
149
|
+
.filter((s) => !SEAT_EXCEPTIONS.includes(s.label))
|
|
150
|
+
.map((val, key) => (React.createElement("span", { key: key, className: `flex items-center justify-between text-[13.33px] ${isSoldOut ? "text-[#c0c0c0]" : ""}` }, CommonService.truncateSeatLabel(val.label))));
|
|
151
|
+
}
|
|
152
|
+
// Single seat type → original behaviour
|
|
120
153
|
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
121
154
|
const filteredSeats = seats.filter((s) => !SEAT_EXCEPTIONS.includes(s.label));
|
|
122
155
|
const seatLabel = filteredSeats.length > 0
|
|
@@ -125,7 +158,6 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
|
|
|
125
158
|
: filteredSeats[0].label
|
|
126
159
|
: null;
|
|
127
160
|
const operatorServiceName = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_service_name) === "MovilBus";
|
|
128
|
-
const bottomLabel = operatorServiceName || "Desde";
|
|
129
161
|
return (React.createElement(React.Fragment, null,
|
|
130
162
|
hasDiscount && (React.createElement("span", { className: "text-[13.33px]", style: {
|
|
131
163
|
color: "#999",
|
|
@@ -224,7 +256,7 @@ function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currenc
|
|
|
224
256
|
React.createElement("span", { className: "text-[13.33px] font-normal leading-[22px] text-[#ccc]" }, "Antes")),
|
|
225
257
|
React.createElement("div", { className: "col-start-1 row-start-3 flex h-[20px] items-end" },
|
|
226
258
|
React.createElement("span", { className: "text-[13.33px] font-normal leading-[20px] text-[#464647]" }, "Desde")),
|
|
227
|
-
React.createElement("div", { className: "col-start-2 row-start-1 flex items-center justify-center absolute", style: { top: "-22px", left: "50%", transform: "translateX(-50%)" } }, discountValue != null && (React.createElement("span", { className: "rounded-[100px] bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white", style: {
|
|
259
|
+
React.createElement("div", { className: "col-start-2 row-start-1 flex items-center justify-center absolute", style: { top: "-22px", left: "50%", transform: "translateX(-50%)" } }, discountValue != null && !isMaxDiscountApplied && (React.createElement("span", { className: "rounded-[100px] bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white", style: {
|
|
228
260
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
229
261
|
whiteSpace: "nowrap",
|
|
230
262
|
backgroundColor: discountSeatPriceColor,
|
|
@@ -64,21 +64,29 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
64
64
|
if (lowestFare === null)
|
|
65
65
|
return null;
|
|
66
66
|
const priceColor = isSoldOut ? "#bbb" : seatPriceColor;
|
|
67
|
-
const { originalPrice, discountedPrice } = commonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
68
67
|
const isMovilBus = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_service_name) === "MovilBus";
|
|
69
|
-
|
|
68
|
+
// Fetch ALL unique seats (no slice limit) for the multi-row MovilBus case
|
|
69
|
+
const uniqueSeats = getUniqueSeats(seatTypesData !== null && seatTypesData !== void 0 ? seatTypesData : [], Infinity);
|
|
70
|
+
// MovilBus + multiple unique seat types → render a row per seat
|
|
71
|
+
if (isMovilBus && uniqueSeats.length > 1) {
|
|
72
|
+
return (React.createElement(React.Fragment, null, uniqueSeats.map((seat, key) => {
|
|
73
|
+
const { discountedPrice } = commonService.calculateDiscountedPrice(Number(seat.fare), serviceItem);
|
|
74
|
+
return (React.createElement("div", { key: key, className: "w-[100%] flex flex-row justify-between items-center" },
|
|
75
|
+
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: isSoldOut ? "#bbb" : "#464647" } }, commonService.truncateSeatLabel(seat.label)),
|
|
76
|
+
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(discountedPrice, currencySign))));
|
|
77
|
+
})));
|
|
78
|
+
}
|
|
79
|
+
// Single seat type (or non-MovilBus) → original behaviour
|
|
80
|
+
const { originalPrice, discountedPrice } = commonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
70
81
|
const seatLabel = uniqueSeats.length > 0
|
|
71
82
|
? commonService.truncateSeatLabel(uniqueSeats[0].label)
|
|
72
83
|
: null;
|
|
73
|
-
const bottomLabel = isMovilBus ? seatLabel || "Desde" : "Desde";
|
|
74
84
|
return (React.createElement(React.Fragment, null,
|
|
75
85
|
originalPrice !== discountedPrice && (React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
|
|
76
86
|
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px]", style: { color: "#bbb" } }, "Antes"),
|
|
77
87
|
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] line-through", style: { color: "#bbb" } }, commonService.currency(originalPrice, currencySign)))),
|
|
78
88
|
React.createElement("div", { className: "w-[100%] flex flex-row justify-between items-center" },
|
|
79
|
-
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: isSoldOut ? "#bbb" : "#464647" } },
|
|
80
|
-
? seatLabel
|
|
81
|
-
: "Desde"),
|
|
89
|
+
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: isSoldOut ? "#bbb" : "#464647" } }, isMovilBus ? seatLabel || "Desde" : "Desde"),
|
|
82
90
|
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] bold-text", style: { color: priceColor } }, commonService.currency(discountedPrice, currencySign)))));
|
|
83
91
|
};
|
|
84
92
|
const renderDpSeats = () => {
|
|
@@ -146,6 +154,19 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
146
154
|
}
|
|
147
155
|
return null;
|
|
148
156
|
})();
|
|
157
|
+
// Hide the % OFF badge when max_discount is capping the percentage discount
|
|
158
|
+
const isMaxDiscountApplied = (() => {
|
|
159
|
+
const { discount_type, discount_value, max_discount } = serviceItem !== null && serviceItem !== void 0 ? serviceItem : {};
|
|
160
|
+
if (discount_type === "percentage" &&
|
|
161
|
+
typeof discount_value === "number" &&
|
|
162
|
+
max_discount != null &&
|
|
163
|
+
max_discount > 0 &&
|
|
164
|
+
discountSeat) {
|
|
165
|
+
const rawPercentageDiscount = (discountSeat.originalPrice * discount_value) / 100;
|
|
166
|
+
return rawPercentageDiscount > max_discount;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
})();
|
|
149
170
|
const getMinValue = (data) => {
|
|
150
171
|
const vals = (Array.isArray(data) ? data : Object.values(data || {})).map(Number);
|
|
151
172
|
if (!vals.length)
|
|
@@ -198,7 +219,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
198
219
|
commonService.discountedCurrency(Number(firstSeatFare), currencySign)),
|
|
199
220
|
isSoldOut ? (React.createElement("span", { className: "col-span-2 min-[420]:text-[13px] text-right text-[12px] text-[#ccc]" }, "Agotado")) : null)) : hasDiscount && discountSeat ? (React.createElement("div", null,
|
|
200
221
|
React.createElement("div", { className: "relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] " },
|
|
201
|
-
discountValue != null && (React.createElement("div", { className: "absolute -top-[18px] right-[0px]", style: {
|
|
222
|
+
discountValue != null && !isMaxDiscountApplied && (React.createElement("div", { className: "absolute -top-[18px] right-[0px]", style: {
|
|
202
223
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
203
224
|
opacity: isSoldOut ? 0.5 : 1,
|
|
204
225
|
} },
|
package/package.json
CHANGED
|
@@ -225,6 +225,27 @@ function ServiceItemPB({
|
|
|
225
225
|
(seat) => seat.originalPrice !== seat.discountedPrice,
|
|
226
226
|
);
|
|
227
227
|
|
|
228
|
+
// Mirror the same check as SeatSection: hide badge (and its top margin) when
|
|
229
|
+
// both percentage and max_discount exist and the cap is being applied.
|
|
230
|
+
const isMaxDiscountApplied = (() => {
|
|
231
|
+
const { discount_type, discount_value, max_discount } = serviceItem as any;
|
|
232
|
+
if (
|
|
233
|
+
discount_type === "percentage" &&
|
|
234
|
+
typeof discount_value === "number" &&
|
|
235
|
+
max_discount != null &&
|
|
236
|
+
max_discount > 0
|
|
237
|
+
) {
|
|
238
|
+
const lowestFare = discountedSeats
|
|
239
|
+
.map((s) => s.originalPrice)
|
|
240
|
+
.filter((p) => p > 0)
|
|
241
|
+
.sort((a, b) => a - b)[0];
|
|
242
|
+
if (lowestFare != null) {
|
|
243
|
+
return (lowestFare * discount_value) / 100 > max_discount;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return false;
|
|
247
|
+
})();
|
|
248
|
+
|
|
228
249
|
const dpDiscountEntry = Object.entries(
|
|
229
250
|
serviceItem?.dp_discount_percents || {},
|
|
230
251
|
)[0];
|
|
@@ -532,7 +553,8 @@ function ServiceItemPB({
|
|
|
532
553
|
: "20px 15px 10px 15px",
|
|
533
554
|
|
|
534
555
|
marginTop:
|
|
535
|
-
hasDiscount || hasOfferText || dpDiscountPercent
|
|
556
|
+
(hasDiscount || hasOfferText || dpDiscountPercent) &&
|
|
557
|
+
!isMaxDiscountApplied
|
|
536
558
|
? "14px"
|
|
537
559
|
: "",
|
|
538
560
|
}}
|
|
@@ -139,6 +139,26 @@ function SeatSection({
|
|
|
139
139
|
|
|
140
140
|
const renderSeatPrices = () => {
|
|
141
141
|
if (isPeru) {
|
|
142
|
+
const isMovilBus = serviceItem?.operator_service_name === "MovilBus";
|
|
143
|
+
|
|
144
|
+
// Multiple unique seat types → show a price row for each (MovilBus only)
|
|
145
|
+
if (isMovilBus && uniqueSeats.length > 1) {
|
|
146
|
+
return uniqueSeats
|
|
147
|
+
.filter((s) => !SEAT_EXCEPTIONS.includes(s.label))
|
|
148
|
+
.map((val, key) => {
|
|
149
|
+
const { discountedPrice } = CommonService.calculateDiscountedPrice(
|
|
150
|
+
val.price,
|
|
151
|
+
serviceItem,
|
|
152
|
+
);
|
|
153
|
+
return (
|
|
154
|
+
<span key={key} className="flex items-center text-[13.33px] bold-text">
|
|
155
|
+
{formatPrice(discountedPrice)}
|
|
156
|
+
</span>
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Single seat type → original behaviour (one lowest-fare price)
|
|
142
162
|
const allSeats = getAllSeatTypes(seatTypes);
|
|
143
163
|
const lowestFare = allSeats.length > 0 ? allSeats[0].price : 0;
|
|
144
164
|
const { discountedPrice } = CommonService.calculateDiscountedPrice(
|
|
@@ -216,8 +236,45 @@ function SeatSection({
|
|
|
216
236
|
return null;
|
|
217
237
|
})();
|
|
218
238
|
|
|
239
|
+
// Hide the % OFF badge when max_discount is capping the percentage discount
|
|
240
|
+
// (i.e. both percentage and max_discount exist, and the raw % amount exceeds the cap)
|
|
241
|
+
const isMaxDiscountApplied = (() => {
|
|
242
|
+
const { discount_type, discount_value, max_discount } = serviceItem ?? {};
|
|
243
|
+
if (
|
|
244
|
+
discount_type === "percentage" &&
|
|
245
|
+
typeof discount_value === "number" &&
|
|
246
|
+
max_discount != null &&
|
|
247
|
+
max_discount > 0 &&
|
|
248
|
+
discountSeat
|
|
249
|
+
) {
|
|
250
|
+
const rawPercentageDiscount =
|
|
251
|
+
(discountSeat.originalPrice * discount_value) / 100;
|
|
252
|
+
return rawPercentageDiscount > max_discount;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
})();
|
|
256
|
+
|
|
219
257
|
const renderLabels = () => {
|
|
220
258
|
if (isPeru) {
|
|
259
|
+
const isMovilBus = serviceItem?.operator_service_name === "MovilBus";
|
|
260
|
+
|
|
261
|
+
// Multiple unique seat types → show a label row for each (MovilBus only)
|
|
262
|
+
if (isMovilBus && uniqueSeats.length > 1) {
|
|
263
|
+
return uniqueSeats
|
|
264
|
+
.filter((s) => !SEAT_EXCEPTIONS.includes(s.label))
|
|
265
|
+
.map((val, key) => (
|
|
266
|
+
<span
|
|
267
|
+
key={key}
|
|
268
|
+
className={`flex items-center justify-between text-[13.33px] ${
|
|
269
|
+
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
270
|
+
}`}
|
|
271
|
+
>
|
|
272
|
+
{CommonService.truncateSeatLabel(val.label)}
|
|
273
|
+
</span>
|
|
274
|
+
));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Single seat type → original behaviour
|
|
221
278
|
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
222
279
|
const filteredSeats = seats.filter(
|
|
223
280
|
(s) => !SEAT_EXCEPTIONS.includes(s.label),
|
|
@@ -231,7 +288,6 @@ function SeatSection({
|
|
|
231
288
|
|
|
232
289
|
const operatorServiceName =
|
|
233
290
|
serviceItem?.operator_service_name === "MovilBus";
|
|
234
|
-
const bottomLabel = operatorServiceName || "Desde";
|
|
235
291
|
|
|
236
292
|
return (
|
|
237
293
|
<>
|
|
@@ -452,7 +508,7 @@ function SeatSection({
|
|
|
452
508
|
className="col-start-2 row-start-1 flex items-center justify-center absolute"
|
|
453
509
|
style={{ top: "-22px", left: "50%", transform: "translateX(-50%)" }}
|
|
454
510
|
>
|
|
455
|
-
{discountValue != null && (
|
|
511
|
+
{discountValue != null && !isMaxDiscountApplied && (
|
|
456
512
|
<span
|
|
457
513
|
className="rounded-[100px] bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white"
|
|
458
514
|
style={{
|
|
@@ -149,16 +149,52 @@ function SeatSectionMobile({
|
|
|
149
149
|
if (lowestFare === null) return null;
|
|
150
150
|
|
|
151
151
|
const priceColor = isSoldOut ? "#bbb" : seatPriceColor;
|
|
152
|
+
const isMovilBus = serviceItem?.operator_service_name === "MovilBus";
|
|
153
|
+
|
|
154
|
+
// Fetch ALL unique seats (no slice limit) for the multi-row MovilBus case
|
|
155
|
+
const uniqueSeats = getUniqueSeats(seatTypesData ?? [], Infinity as number);
|
|
156
|
+
|
|
157
|
+
// MovilBus + multiple unique seat types → render a row per seat
|
|
158
|
+
if (isMovilBus && uniqueSeats.length > 1) {
|
|
159
|
+
return (
|
|
160
|
+
<>
|
|
161
|
+
{uniqueSeats.map((seat, key) => {
|
|
162
|
+
const { discountedPrice } = commonService.calculateDiscountedPrice(
|
|
163
|
+
Number(seat.fare),
|
|
164
|
+
serviceItem,
|
|
165
|
+
);
|
|
166
|
+
return (
|
|
167
|
+
<div
|
|
168
|
+
key={key}
|
|
169
|
+
className="w-[100%] flex flex-row justify-between items-center"
|
|
170
|
+
>
|
|
171
|
+
<span
|
|
172
|
+
className="min-[420]:text-[13px] text-[12px] bold-text"
|
|
173
|
+
style={{ color: isSoldOut ? "#bbb" : "#464647" }}
|
|
174
|
+
>
|
|
175
|
+
{commonService.truncateSeatLabel(seat.label)}
|
|
176
|
+
</span>
|
|
177
|
+
<span
|
|
178
|
+
className="min-[420]:text-[13px] text-[12px] bold-text"
|
|
179
|
+
style={{ color: priceColor }}
|
|
180
|
+
>
|
|
181
|
+
{commonService.currency(discountedPrice, currencySign)}
|
|
182
|
+
</span>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
})}
|
|
186
|
+
</>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Single seat type (or non-MovilBus) → original behaviour
|
|
152
191
|
const { originalPrice, discountedPrice } =
|
|
153
192
|
commonService.calculateDiscountedPrice(lowestFare, serviceItem);
|
|
154
193
|
|
|
155
|
-
const isMovilBus = serviceItem?.operator_service_name === "MovilBus";
|
|
156
|
-
const uniqueSeats = getUniqueSeats(seatTypesData ?? [], 1);
|
|
157
194
|
const seatLabel =
|
|
158
195
|
uniqueSeats.length > 0
|
|
159
196
|
? commonService.truncateSeatLabel(uniqueSeats[0].label)
|
|
160
197
|
: null;
|
|
161
|
-
const bottomLabel = isMovilBus ? seatLabel || "Desde" : "Desde";
|
|
162
198
|
|
|
163
199
|
return (
|
|
164
200
|
<>
|
|
@@ -183,9 +219,7 @@ function SeatSectionMobile({
|
|
|
183
219
|
className="min-[420]:text-[13px] text-[12px] bold-text"
|
|
184
220
|
style={{ color: isSoldOut ? "#bbb" : "#464647" }}
|
|
185
221
|
>
|
|
186
|
-
{
|
|
187
|
-
? seatLabel
|
|
188
|
-
: "Desde"}
|
|
222
|
+
{isMovilBus ? seatLabel || "Desde" : "Desde"}
|
|
189
223
|
</span>
|
|
190
224
|
<span
|
|
191
225
|
className="min-[420]:text-[13px] text-[12px] bold-text"
|
|
@@ -359,6 +393,23 @@ function SeatSectionMobile({
|
|
|
359
393
|
return null;
|
|
360
394
|
})();
|
|
361
395
|
|
|
396
|
+
// Hide the % OFF badge when max_discount is capping the percentage discount
|
|
397
|
+
const isMaxDiscountApplied = (() => {
|
|
398
|
+
const { discount_type, discount_value, max_discount } = serviceItem ?? {};
|
|
399
|
+
if (
|
|
400
|
+
discount_type === "percentage" &&
|
|
401
|
+
typeof discount_value === "number" &&
|
|
402
|
+
max_discount != null &&
|
|
403
|
+
max_discount > 0 &&
|
|
404
|
+
discountSeat
|
|
405
|
+
) {
|
|
406
|
+
const rawPercentageDiscount =
|
|
407
|
+
(discountSeat.originalPrice * discount_value) / 100;
|
|
408
|
+
return rawPercentageDiscount > max_discount;
|
|
409
|
+
}
|
|
410
|
+
return false;
|
|
411
|
+
})();
|
|
412
|
+
|
|
362
413
|
const getMinValue = (data: any): number | undefined => {
|
|
363
414
|
const vals = (Array.isArray(data) ? data : Object.values(data || {})).map(
|
|
364
415
|
Number,
|
|
@@ -476,7 +527,7 @@ function SeatSectionMobile({
|
|
|
476
527
|
) : hasDiscount && discountSeat ? (
|
|
477
528
|
<div>
|
|
478
529
|
<div className="relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] ">
|
|
479
|
-
{discountValue != null && (
|
|
530
|
+
{discountValue != null && !isMaxDiscountApplied && (
|
|
480
531
|
<div
|
|
481
532
|
className="absolute -top-[18px] right-[0px]"
|
|
482
533
|
style={{
|