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.
- package/dist/components/FilterBar/FilterBarDesktop.js +10 -0
- package/dist/components/FilterBar/ServiceFilter.d.ts +1 -0
- package/dist/components/FilterBar/ServiceFilter.js +10 -4
- package/dist/components/ServiceItem/PeruServiceItemDesktop.js +3 -1
- package/dist/components/ServiceItem/PeruServiceItemMobile.js +5 -3
- package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemDesktop.js +7 -5
- package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemMobile.js +14 -16
- package/dist/components/ServiceItem/mobileTypes.d.ts +3 -0
- package/dist/components/ServiceItem/types.d.ts +3 -0
- package/dist/styles.css +3 -0
- package/dist/ui/OfferBanner.d.ts +1 -1
- package/dist/ui/OfferBanner.js +9 -5
- package/dist/ui/PeruOfferBanner.d.ts +1 -1
- package/dist/ui/PeruOfferBanner.js +5 -1
- package/dist/ui/RatingBlock.d.ts +1 -1
- package/dist/ui/RatingBlock.js +7 -3
- package/dist/ui/SeatSection/SeatSection.js +28 -13
- package/dist/ui/ServiceBadges/ServiceBadges.js +1 -1
- package/dist/ui/mobileweb/SeatSectionMobile.js +10 -5
- package/dist/utils/CommonService.d.ts +7 -0
- package/dist/utils/CommonService.js +6 -0
- package/package.json +1 -1
- package/src/components/FilterBar/FilterBarDesktop.tsx +16 -0
- package/src/components/FilterBar/ServiceFilter.tsx +12 -2
- package/src/components/ServiceItem/PeruServiceItemDesktop.tsx +4 -1
- package/src/components/ServiceItem/PeruServiceItemMobile.tsx +6 -3
- package/src/components/ServiceItem/ServiceItemDesktop.tsx +9 -3
- package/src/components/ServiceItem/ServiceItemMobile.tsx +25 -14
- package/src/components/ServiceItem/mobileTypes.ts +3 -0
- package/src/components/ServiceItem/types.ts +3 -0
- package/src/ui/OfferBanner.tsx +10 -5
- package/src/ui/PeruOfferBanner.tsx +7 -1
- package/src/ui/RatingBlock.tsx +7 -2
- package/src/ui/SeatSection/SeatSection.tsx +53 -29
- package/src/ui/ServiceBadges/ServiceBadges.tsx +1 -1
- package/src/ui/mobileweb/SeatSectionMobile.tsx +30 -20
- package/src/utils/CommonService.ts +13 -0
|
@@ -119,6 +119,13 @@ function getUniqueSeats(seatTypes: SeatType[], isTrain?: boolean) {
|
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Matches "160°" as well as a bare "160" label
|
|
123
|
+
function hasInclinationSeatTypes(seatTypes: SeatType[]) {
|
|
124
|
+
return (seatTypes ?? []).some((seat) =>
|
|
125
|
+
/\b(120|140|160|180)\b/.test(String(seat?.label ?? "")),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
122
129
|
function getNumberOfSeats(seatTypes: SeatType[]) {
|
|
123
130
|
return seatTypes.filter((val) => !SEAT_EXCEPTIONS.includes(val.label)).length;
|
|
124
131
|
}
|
|
@@ -169,6 +176,14 @@ function SeatSection({
|
|
|
169
176
|
const uniqueSeats = getUniqueSeats(seatTypes, isTrain);
|
|
170
177
|
const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
|
|
171
178
|
const numberOfSeats = getNumberOfSeats(seatTypes);
|
|
179
|
+
const isMovilBus =
|
|
180
|
+
serviceItem?.operator_service_name === "MovilBus" ||
|
|
181
|
+
serviceItem?.operator_service_name === "Movil Bus";
|
|
182
|
+
// Peru: list every seat type with its own fare (MovilBus, or inclination
|
|
183
|
+
// seats like 160° / 180°) instead of only the cheapest one.
|
|
184
|
+
const showSeatTypeRows =
|
|
185
|
+
(isMovilBus || hasInclinationSeatTypes(seatTypes)) &&
|
|
186
|
+
uniqueSeats.length > 1;
|
|
172
187
|
const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
|
|
173
188
|
|
|
174
189
|
const formatPrice = (price: number) =>
|
|
@@ -256,12 +271,8 @@ function SeatSection({
|
|
|
256
271
|
|
|
257
272
|
const renderSeatPrices = () => {
|
|
258
273
|
if (isPeru) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
serviceItem?.operator_service_name === "Movil Bus";
|
|
262
|
-
|
|
263
|
-
// Multiple unique seat types → show a price row for each (MovilBus only)
|
|
264
|
-
if (isMovilBus && uniqueSeats.length > 1) {
|
|
274
|
+
// Multiple unique seat types → show a price row for each
|
|
275
|
+
if (showSeatTypeRows) {
|
|
265
276
|
return uniqueSeats
|
|
266
277
|
.filter((s) => !SEAT_EXCEPTIONS.includes(s.label))
|
|
267
278
|
.map((val, key) => {
|
|
@@ -378,12 +389,8 @@ function SeatSection({
|
|
|
378
389
|
|
|
379
390
|
const renderLabels = () => {
|
|
380
391
|
if (isPeru) {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
serviceItem?.operator_service_name === "Movil Bus";
|
|
384
|
-
|
|
385
|
-
// Multiple unique seat types → show a label row for each (MovilBus only)
|
|
386
|
-
if (isMovilBus && uniqueSeats.length > 1) {
|
|
392
|
+
// Multiple unique seat types → show a label row for each
|
|
393
|
+
if (showSeatTypeRows) {
|
|
387
394
|
return uniqueSeats
|
|
388
395
|
.filter((s) => !SEAT_EXCEPTIONS.includes(s.label))
|
|
389
396
|
.map((val, key) => (
|
|
@@ -404,7 +411,20 @@ function SeatSection({
|
|
|
404
411
|
}}
|
|
405
412
|
/>
|
|
406
413
|
)}
|
|
407
|
-
{
|
|
414
|
+
{!isMovilBus && isShowSeatIcon && (
|
|
415
|
+
<img
|
|
416
|
+
src={getSeatLabelIcon(val.label, serviceItem)}
|
|
417
|
+
alt="seattype"
|
|
418
|
+
style={{
|
|
419
|
+
width: "20px",
|
|
420
|
+
height: "20px",
|
|
421
|
+
marginRight: "5px",
|
|
422
|
+
}}
|
|
423
|
+
/>
|
|
424
|
+
)}
|
|
425
|
+
{isMovilBus
|
|
426
|
+
? CommonService.truncateSeatLabel(val.label)
|
|
427
|
+
: CommonService.capitalize(addSeatDegree(val.label))}
|
|
408
428
|
</span>
|
|
409
429
|
));
|
|
410
430
|
}
|
|
@@ -496,6 +516,8 @@ function SeatSection({
|
|
|
496
516
|
serviceItem?.dp_discount_percents || {},
|
|
497
517
|
).sort(([, a], [, b]) => Number(a) - Number(b))[0];
|
|
498
518
|
const dpDiscountPercent = dpDiscountEntry?.[1];
|
|
519
|
+
// show_dp_ui: false falls through to the regular fare layout
|
|
520
|
+
const isDpUiHidden = CommonService.isDpUiHidden(serviceItem);
|
|
499
521
|
const getLowestDpValue = (source: any): number | undefined => {
|
|
500
522
|
if (!source) return undefined;
|
|
501
523
|
const values: any[] = Array.isArray(source)
|
|
@@ -591,7 +613,7 @@ function SeatSection({
|
|
|
591
613
|
);
|
|
592
614
|
};
|
|
593
615
|
|
|
594
|
-
if (serviceItem?.is_dp_enabled && !dpDiscountEntry) {
|
|
616
|
+
if (serviceItem?.is_dp_enabled && !isDpUiHidden && !dpDiscountEntry) {
|
|
595
617
|
const dpSeats = (
|
|
596
618
|
removeDuplicateSeats ? uniqueSeats : sortedSeatTypes
|
|
597
619
|
).filter((s) => !SEAT_EXCEPTIONS.includes(s.label));
|
|
@@ -616,25 +638,27 @@ function SeatSection({
|
|
|
616
638
|
<div className="absolute -left-[20px] transform -translate-y-1/2 top-[16px]">
|
|
617
639
|
{renderIcon("fireIcon", "16px")}
|
|
618
640
|
</div>
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
623
|
-
whiteSpace: "nowrap",
|
|
624
|
-
}}
|
|
625
|
-
>
|
|
626
|
-
<span
|
|
627
|
-
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 "
|
|
628
644
|
style={{
|
|
629
645
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
630
646
|
whiteSpace: "nowrap",
|
|
631
|
-
color: dpSeatColor,
|
|
632
|
-
backgroundColor: tooltipColor,
|
|
633
647
|
}}
|
|
634
648
|
>
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
+
)}
|
|
638
662
|
<div
|
|
639
663
|
className="text-[22px] bold-text leading-[30px] text-center"
|
|
640
664
|
style={{ color: isSoldOut ? "#c0c0c0" : dpSeatColor }}
|
|
@@ -649,7 +673,7 @@ function SeatSection({
|
|
|
649
673
|
);
|
|
650
674
|
}
|
|
651
675
|
|
|
652
|
-
if (dpDiscountEntry) {
|
|
676
|
+
if (dpDiscountEntry && !isDpUiHidden) {
|
|
653
677
|
const originalDpPrice = getLowestDpValue(serviceItem.original_dp_price);
|
|
654
678
|
const allFares = (serviceItem.seat_types ?? [])
|
|
655
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-[
|
|
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`}
|
|
@@ -112,8 +112,9 @@ function addSeatDegree(label: string | null) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
function hasInclinationSeatTypes(seatTypes: SeatType[]): boolean {
|
|
115
|
+
// Matches "160°" as well as a bare "160" label
|
|
115
116
|
return (seatTypes ?? []).some((seat) =>
|
|
116
|
-
String(seat?.label ?? "")
|
|
117
|
+
/\b(120|140|160|180)\b/.test(String(seat?.label ?? "")),
|
|
117
118
|
);
|
|
118
119
|
}
|
|
119
120
|
|
|
@@ -247,7 +248,9 @@ function SeatSectionMobile({
|
|
|
247
248
|
}}
|
|
248
249
|
/>
|
|
249
250
|
)}
|
|
250
|
-
{commonService.truncateSeatLabel(
|
|
251
|
+
{commonService.truncateSeatLabel(
|
|
252
|
+
commonService.addSeatDegree(seat.label),
|
|
253
|
+
)}
|
|
251
254
|
</span>
|
|
252
255
|
<span
|
|
253
256
|
className="min-[420]:text-[13px] text-[12px] bold-text"
|
|
@@ -357,27 +360,29 @@ function SeatSectionMobile({
|
|
|
357
360
|
className="relative flex flex-col justify-center h-full"
|
|
358
361
|
style={{ opacity: isSoldOut ? 0.5 : 1 }}
|
|
359
362
|
>
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
364
|
-
top:
|
|
365
|
-
(serviceItem?.available_seats < 10 &&
|
|
366
|
-
serviceItem?.available_seats > 0) ||
|
|
367
|
-
isSoldOut
|
|
368
|
-
? "-20px"
|
|
369
|
-
: "-15px",
|
|
370
|
-
}}
|
|
371
|
-
>
|
|
372
|
-
<span
|
|
373
|
-
className="rounded-[100px] px-[8px] text-[11px] bold-text leading-[20px] text-[#fff]"
|
|
363
|
+
{commonService.showDpBestPrice(serviceItem) && (
|
|
364
|
+
<div
|
|
365
|
+
className="absolute right-[0px]"
|
|
374
366
|
style={{
|
|
375
|
-
|
|
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",
|
|
376
374
|
}}
|
|
377
375
|
>
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
+
)}
|
|
381
386
|
<div className="w-[100%] flex flex-row justify-between items-center">
|
|
382
387
|
<span
|
|
383
388
|
className="min-[420]:text-[13px] text-[12px]"
|
|
@@ -565,7 +570,11 @@ function SeatSectionMobile({
|
|
|
565
570
|
)
|
|
566
571
|
?.sort((a, b) => a.fare - b.fare)[0]?.fare;
|
|
567
572
|
|
|
573
|
+
// show_dp_ui: false falls through to the regular fare layout
|
|
574
|
+
const isDpUiHidden = commonService.isDpUiHidden(serviceItem);
|
|
575
|
+
|
|
568
576
|
const hasDpDiscount =
|
|
577
|
+
!isDpUiHidden &&
|
|
569
578
|
serviceItem?.dp_discounted_seats &&
|
|
570
579
|
serviceItem?.dp_discount_percents &&
|
|
571
580
|
originalDpPrice != null &&
|
|
@@ -578,6 +587,7 @@ function SeatSectionMobile({
|
|
|
578
587
|
style={{ width: "40%", flexShrink: 0 }}
|
|
579
588
|
>
|
|
580
589
|
{serviceItem?.is_dp_enabled &&
|
|
590
|
+
!isDpUiHidden &&
|
|
581
591
|
!serviceItem?.dp_discounted_seats?.length &&
|
|
582
592
|
!dpDiscountPercent ? (
|
|
583
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?: {
|