kupos-ui-components-lib 10.4.29 → 10.4.30

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.
@@ -236,14 +236,19 @@ function PeruSeatSection({
236
236
  .filter((seat) => !SEAT_EXCEPTIONS.includes(seat.label))
237
237
  .sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
238
238
 
239
+ // discount_type/discount_value/max_discount live on the service item
240
+ // itself; only ui_type and the ready-made badge text come from discounts[0].
241
+ const peruDiscount = serviceItem?.discounts?.[0];
242
+ const uiType = peruDiscount?.ui_type;
243
+ const discountType = serviceItem?.discount_type;
244
+ const discountValueRaw = serviceItem?.discount_value;
245
+ const maxDiscount = serviceItem?.max_discount;
246
+
239
247
  const discountValue = (() => {
240
- if (
241
- serviceItem?.discount_type === "percentage" &&
242
- typeof serviceItem?.discount_value === "number"
243
- ) {
244
- return Math.round(serviceItem.discount_value);
248
+ if (discountType === "percentage" && typeof discountValueRaw === "number") {
249
+ return Math.round(discountValueRaw);
245
250
  }
246
- if (serviceItem?.discount_type === "fixed" && discountSeat) {
251
+ if (discountType === "fixed" && discountSeat) {
247
252
  const { originalPrice, discountedPrice } = discountSeat;
248
253
  if (originalPrice > 0 && originalPrice !== discountedPrice) {
249
254
  return Math.round(
@@ -255,21 +260,30 @@ function PeruSeatSection({
255
260
  })();
256
261
 
257
262
  const isMaxDiscountApplied = (() => {
258
- const { discount_type, discount_value, max_discount } = serviceItem ?? {};
259
263
  if (
260
- discount_type === "percentage" &&
261
- typeof discount_value === "number" &&
262
- max_discount != null &&
263
- max_discount > 0 &&
264
+ discountType === "percentage" &&
265
+ typeof discountValueRaw === "number" &&
266
+ maxDiscount != null &&
267
+ maxDiscount > 0 &&
264
268
  discountSeat
265
269
  ) {
266
270
  const rawPercentageDiscount =
267
- (discountSeat.originalPrice * discount_value) / 100;
268
- return rawPercentageDiscount > max_discount;
271
+ (discountSeat.originalPrice * discountValueRaw) / 100;
272
+ return rawPercentageDiscount > maxDiscount;
269
273
  }
270
274
  return false;
271
275
  })();
272
276
 
277
+ // Backend-provided ready-made badge text (e.g. "30% OFF" / "$2.000 OFF")
278
+ // takes precedence over the value computed client-side.
279
+ const discountBadgeText =
280
+ peruDiscount?.discount_display_badge ||
281
+ (isMaxDiscountApplied
282
+ ? `${CommonService.currency(maxDiscount)} OFF`
283
+ : discountValue != null
284
+ ? `${discountValue}% OFF`
285
+ : null);
286
+
273
287
  const renderLabels = () => {
274
288
  const isMovilBus =
275
289
  serviceItem?.operator_service_name === "MovilBus" ||
@@ -363,6 +377,41 @@ function PeruSeatSection({
363
377
  };
364
378
 
365
379
  if (hasDiscount && discountSeat) {
380
+ // Tier B (ui_type 1): offer text is announced elsewhere, but the fare
381
+ // itself must render exactly as it would with no discount — no
382
+ // strike-off, no badge, no fire icon.
383
+ if (uiType === 1) {
384
+ return (
385
+ <div
386
+ className="grid grid-cols-2 items-center text-[13.33px] relative"
387
+ style={{ opacity: isSoldOut ? 0.5 : 1 }}
388
+ >
389
+ <div className="col-start-1 row-start-3 flex h-[20px] items-end">
390
+ <span className="text-[13.33px] font-normal leading-[20px] text-[#464647]">
391
+ {discountSeat.label}
392
+ </span>
393
+ </div>
394
+
395
+ <div className="col-start-2 row-start-3 flex h-[30px] items-end justify-center relative">
396
+ <span
397
+ className="flex items-center gap-[6px] bold-text leading-[30px]"
398
+ style={{
399
+ color: isSoldOut ? "#c0c0c0" : "#464647",
400
+ fontSize: "13.33px",
401
+ }}
402
+ >
403
+ {availableSeats <= 0
404
+ ? CommonService.currency(0, currencySign)
405
+ : CommonService.discountedCurrency(
406
+ discountSeat.originalPrice,
407
+ currencySign,
408
+ )}
409
+ </span>
410
+ </div>
411
+ </div>
412
+ );
413
+ }
414
+
366
415
  return (
367
416
  <div
368
417
  className="grid grid-cols-2 items-center text-[13.33px] relative"
@@ -384,7 +433,7 @@ function PeruSeatSection({
384
433
  className="col-start-2 row-start-1 flex items-center justify-center absolute"
385
434
  style={{ top: "-22px", left: "50%", transform: "translateX(-50%)" }}
386
435
  >
387
- {discountValue != null || isMaxDiscountApplied ? (
436
+ {discountBadgeText ? (
388
437
  <span
389
438
  className="rounded-[100px] bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white"
390
439
  style={{
@@ -393,9 +442,7 @@ function PeruSeatSection({
393
442
  backgroundColor: discountSeatPriceColor,
394
443
  }}
395
444
  >
396
- {isMaxDiscountApplied
397
- ? `${CommonService.currency(serviceItem?.max_discount)} OFF`
398
- : `${discountValue}% OFF`}
445
+ {discountBadgeText}
399
446
  </span>
400
447
  ) : null}
401
448
  </div>
@@ -478,6 +478,16 @@ function SeatSectionMobile({
478
478
  return false;
479
479
  })();
480
480
 
481
+ // Backend-provided ready-made badge text (e.g. "30% OFF" / "$2.000 OFF")
482
+ // takes precedence over the value computed client-side.
483
+ const discountBadgeText =
484
+ serviceItem?.discounts?.[0]?.discount_display_badge ||
485
+ (isMaxDiscountApplied
486
+ ? `${commonService.currency(serviceItem?.max_discount)} OFF`
487
+ : discountValue != null
488
+ ? `${discountValue}% OFF`
489
+ : null);
490
+
481
491
  const getMinValue = (data: any): number | undefined => {
482
492
  const vals = (Array.isArray(data) ? data : Object.values(data || {})).map(
483
493
  Number,
@@ -595,59 +605,56 @@ function SeatSectionMobile({
595
605
  ) : hasDiscount && discountSeat ? (
596
606
  <div>
597
607
  <div className="relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] ">
598
- {serviceItem?.ui_type !== 1 &&
599
- (discountValue != null || isMaxDiscountApplied) && (
600
- <>
601
- <div
602
- className="absolute -top-[18px] right-[0px]"
608
+ {serviceItem?.ui_type !== 1 && discountBadgeText && (
609
+ <>
610
+ <div
611
+ className="absolute -top-[18px] right-[0px]"
612
+ style={{
613
+ animation: "pulse-zoom 2s ease-in-out infinite",
614
+ opacity: isSoldOut ? 0.5 : 1,
615
+ }}
616
+ >
617
+ <span
618
+ className="rounded-[100px] px-[8px] text-[12px] bold-text leading-[20px] text-[#fff]"
603
619
  style={{
604
- animation: "pulse-zoom 2s ease-in-out infinite",
605
- opacity: isSoldOut ? 0.5 : 1,
620
+ backgroundColor: tooltipBgColor,
606
621
  }}
607
622
  >
608
- <span
609
- className="rounded-[100px] px-[8px] text-[12px] bold-text leading-[20px] text-[#fff]"
610
- style={{
611
- backgroundColor: tooltipBgColor,
612
- }}
613
- >
614
- {isMaxDiscountApplied
615
- ? `${commonService.currency(serviceItem?.max_discount)} OFF`
616
- : `${discountValue}% OFF`}
617
- </span>
618
- </div>
619
-
620
- <span className="min-[420]:text-[13px] text-[12px] leading-[20px] text-[#c2c2c2]">
621
- Antes
623
+ {discountBadgeText}
622
624
  </span>
625
+ </div>
626
+
627
+ <span className="min-[420]:text-[13px] text-[12px] leading-[20px] text-[#c2c2c2]">
628
+ Antes
629
+ </span>
630
+ <span
631
+ className="min-[420]:text-[13px] text-[12px] leading-[20px] text-right "
632
+ style={{
633
+ position: "relative",
634
+ display: "inline-block",
635
+ overflow: "hidden",
636
+ color: discountSeatPriceColor,
637
+ }}
638
+ >
639
+ {commonService.currency(
640
+ discountSeat.originalPrice,
641
+ currencySign,
642
+ )}
623
643
  <span
624
- className="min-[420]:text-[13px] text-[12px] leading-[20px] text-right"
625
644
  style={{
626
- position: "relative",
627
- display: "inline-block",
628
- overflow: "hidden",
629
- color: discountSeatPriceColor,
645
+ position: "absolute",
646
+ top: "35%",
647
+ left: "50%",
648
+ width: "80%",
649
+ height: "1px",
650
+ background: discountSeatCrossPriceColor,
651
+ transform: "rotate(-12deg)",
652
+ transformOrigin: "center",
630
653
  }}
631
- >
632
- {commonService.currency(
633
- discountSeat.originalPrice,
634
- currencySign,
635
- )}
636
- <span
637
- style={{
638
- position: "absolute",
639
- top: "35%",
640
- left: "50%",
641
- width: "80%",
642
- height: "1px",
643
- background: discountSeatCrossPriceColor,
644
- transform: "rotate(-12deg)",
645
- transformOrigin: "center",
646
- }}
647
- />
648
- </span>
649
- </>
650
- )}
654
+ />
655
+ </span>
656
+ </>
657
+ )}
651
658
 
652
659
  <span
653
660
  className="min-[420]:text-[13px] text-[12px] leading-[24px]"