kupos-ui-components-lib 9.3.1 → 9.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/assets/images/anims/service_list/60_percent_anim.json +1 -0
  2. package/dist/assets/images/anims/service_list/dot_animation.json +1 -0
  3. package/dist/assets/images/anims/service_list/thunder_icon.json +1 -0
  4. package/dist/components/ServiceItem/ServiceItemDesktop.js +50 -121
  5. package/dist/components/ServiceItem/ServiceItemMobile.js +44 -105
  6. package/dist/components/ServiceItem/mobileTypes.d.ts +3 -0
  7. package/dist/components/ServiceItem/types.d.ts +2 -0
  8. package/dist/styles.css +125 -23
  9. package/dist/types.d.ts +2 -0
  10. package/dist/ui/ExpendedDropDown/ExpandedDropdown.js +16 -19
  11. package/dist/ui/FeatureServiceUI/FeatureServiceUi.d.ts +12 -0
  12. package/dist/ui/FeatureServiceUI/FeatureServiceUi.js +101 -0
  13. package/dist/ui/SeatSection/SeatSection.d.ts +2 -1
  14. package/dist/ui/SeatSection/SeatSection.js +41 -10
  15. package/dist/ui/ServiceBadges/ServiceBadges.d.ts +17 -0
  16. package/dist/ui/ServiceBadges/ServiceBadges.js +33 -0
  17. package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +2 -1
  18. package/dist/ui/mobileweb/DateTimeSectionMobile.js +2 -2
  19. package/dist/ui/mobileweb/ExpandedDropdownMobile.js +18 -18
  20. package/dist/ui/mobileweb/SeatSectionMobile.d.ts +2 -1
  21. package/dist/ui/mobileweb/SeatSectionMobile.js +28 -16
  22. package/dist/ui/mobileweb/ServiceBadgesMobile.d.ts +17 -0
  23. package/dist/ui/mobileweb/ServiceBadgesMobile.js +35 -0
  24. package/dist/utils/CommonService.d.ts +7 -0
  25. package/dist/utils/CommonService.js +61 -0
  26. package/package.json +1 -1
  27. package/src/assets/images/anims/service_list/dot_animation.json +1 -0
  28. package/src/components/ServiceItem/ServiceItemDesktop.tsx +93 -235
  29. package/src/components/ServiceItem/ServiceItemMobile.tsx +118 -166
  30. package/src/components/ServiceItem/mobileTypes.ts +3 -0
  31. package/src/components/ServiceItem/types.ts +2 -0
  32. package/src/styles.css +10 -0
  33. package/src/types.ts +2 -0
  34. package/src/ui/ExpendedDropDown/ExpandedDropdown.tsx +26 -67
  35. package/src/ui/SeatSection/SeatSection.tsx +87 -32
  36. package/src/ui/ServiceBadges/ServiceBadges.tsx +92 -0
  37. package/src/ui/mobileweb/DateTimeSectionMobile.tsx +3 -0
  38. package/src/ui/mobileweb/ExpandedDropdownMobile.tsx +24 -24
  39. package/src/ui/mobileweb/SeatSectionMobile.tsx +77 -32
  40. package/src/ui/mobileweb/ServiceBadgesMobile.tsx +92 -0
  41. package/src/utils/CommonService.ts +86 -0
@@ -24,18 +24,18 @@ import opsitesLocationAnimation from "../../assets/images/anims/service_list/ops
24
24
  import opsitesPriorityStageAnimation from "../../assets/images/anims/service_list/opsitesPriorityStage.json";
25
25
 
26
26
  import bombAnimation from "../../assets/images/anims/service_list/bomb.json";
27
+ import dotAnimation from "../../assets/images/anims/service_list/dot_animation.json";
27
28
 
28
29
  import RatingBlock from "../../ui/RatingBlock";
29
30
  import DurationBlock from "../../ui/DurationBlock";
30
- import DirectoBlock from "../../ui/DirectoBlock";
31
31
  import PetBlock from "../../ui/PetBlock";
32
32
  import FlexibleBlock from "../../ui/FlexibleBlock";
33
33
  import AmenitiesBlock from "../../ui/AmenitiesBlock";
34
34
  import KuposButton from "../../ui/KuposButton/KuposButton";
35
- import TopAmenities from "../../ui/TopAmenities/TopAmenities";
36
35
  import BottomAmenities from "../../ui/BottomAmenities/BottomAmenities";
37
36
  import SeatSection from "../../ui/SeatSection/SeatSection";
38
37
  import DateTimeSection from "../../ui/DateTimeSection/DateTimeSection";
38
+ import ServiceBadges from "../../ui/ServiceBadges/ServiceBadges";
39
39
 
40
40
  const SEAT_EXCEPTIONS = ["Asiento mascota"];
41
41
 
@@ -69,6 +69,9 @@ const ANIMATION_MAP: Record<string, Record<string, any>> = {
69
69
  bombAnimation: {
70
70
  kupos: bombAnimation,
71
71
  },
72
+ dotAnimation: {
73
+ kupos: dotAnimation,
74
+ },
72
75
  };
73
76
 
74
77
  function ServiceItemPB({
@@ -103,28 +106,6 @@ function ServiceItemPB({
103
106
  coachKey,
104
107
  viewersConfig,
105
108
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
106
- const startViewerCount = (node: HTMLSpanElement | null) => {
107
- if (!node || !viewersConfig) return;
108
-
109
- const prevId = node.dataset.viewerId;
110
- if (prevId) clearInterval(Number(prevId));
111
-
112
- const { min, max, interval = 5000 } = viewersConfig;
113
- const clamp = (v: number) => Math.min(max, Math.max(min, v));
114
- const initialValue = Math.floor(Math.random() * (max - min + 1)) + min;
115
-
116
- node.textContent = String(initialValue);
117
-
118
- const id = setInterval(() => {
119
- const current = Number(node.textContent) || initialValue;
120
- const delta = Math.ceil(current * 0.2);
121
- const next =
122
- current + Math.floor(Math.random() * (2 * delta + 1)) - delta;
123
- node.textContent = String(clamp(Math.round(next)));
124
- }, interval);
125
-
126
- node.dataset.viewerId = String(id);
127
- };
128
109
  const getAnimationIcon = (icon: string) => {
129
110
  const animation = ANIMATION_MAP[icon];
130
111
  if (!animation) return null;
@@ -183,10 +164,19 @@ function ServiceItemPB({
183
164
 
184
165
  let isSoldOut = serviceItem.available_seats <= 0;
185
166
 
186
- const showPromo = Math.random() > 0.5;
187
-
188
167
  const isItemExpanded = serviceItem.id === isExpand || isExpand === true;
189
168
  const grayscaleClass = isSoldOut ? "grayscale" : "";
169
+ const hasOfferText = Boolean(serviceItem?.offer_text);
170
+ const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
171
+ const serviceCardStyle: React.CSSProperties = hasOfferText
172
+ ? {
173
+ borderColor: "transparent",
174
+ borderStyle: "solid",
175
+ borderWidth: "6px 6px 0 6px",
176
+ borderRadius: isItemExpanded || coachKey ? "18px 18px 0 0" : "18px",
177
+ background: `linear-gradient(#fff, #fff) padding-box, ${offerGradient} border-box`,
178
+ }
179
+ : {};
190
180
 
191
181
  const renderIcon = (iconKey: string, size: string = "14px") => {
192
182
  const iconValue = serviceItem.icons?.[iconKey];
@@ -288,34 +278,6 @@ function ServiceItemPB({
288
278
 
289
279
  onBookButtonPress();
290
280
  };
291
- const countdownSeconds = 599;
292
-
293
- const startCountdown = (node: HTMLSpanElement | null) => {
294
- if (!node) return;
295
-
296
- const prevId = node.dataset.countdownId;
297
- if (prevId) clearInterval(Number(prevId));
298
-
299
- let remaining = countdownSeconds;
300
-
301
- const formatTime = (totalSecs: number) => {
302
- const m = Math.floor(totalSecs / 60);
303
- const s = totalSecs % 60;
304
- return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
305
- };
306
-
307
- node.textContent = formatTime(remaining);
308
-
309
- const id = setInterval(() => {
310
- remaining -= 1;
311
- if (remaining <= 0) {
312
- remaining = countdownSeconds;
313
- }
314
- node.textContent = formatTime(remaining);
315
- }, 1000);
316
-
317
- node.dataset.countdownId = String(id);
318
- };
319
281
 
320
282
  const items = [
321
283
  {
@@ -350,20 +312,6 @@ function ServiceItemPB({
350
312
  ),
351
313
  },
352
314
 
353
- // {
354
- // key: "directo",
355
- // width: "12%",
356
- // condition: serviceItem?.is_direct_trip === true,
357
- // render: (
358
- // <DirectoBlock
359
- // translation={translation}
360
- // getAnimationIcon={getAnimationIcon}
361
- // colors={colors}
362
- // isSoldOut={isSoldOut}
363
- // />
364
- // ),
365
- // },
366
-
367
315
  {
368
316
  key: "pet",
369
317
  width: "20%",
@@ -432,13 +380,7 @@ function ServiceItemPB({
432
380
  />
433
381
  ) : (
434
382
  <div
435
- // className={`relative ${
436
- // serviceItem.offer_text ? "mb-[55px]" : "mb-[10px]"
437
- // } mt-[14px]`}
438
-
439
- className={`relative ${
440
- serviceItem.offer_text ? "mb-[55px]" : "mb-[10px]"
441
- } ${
383
+ className={`relative ${hasOfferText ? "mb-[55px]" : "mb-[10px]"} ${
442
384
  serviceItem?.is_direct_trip ||
443
385
  serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
444
386
  showTopLabel
@@ -446,41 +388,14 @@ function ServiceItemPB({
446
388
  : "mt-[20px]"
447
389
  } `}
448
390
  >
449
- {/* <TopAmenities
450
- showPromo={showPromo}
451
- showTopLabel={showTopLabel}
452
- isSoldOut={isSoldOut}
453
- priceColor={colors.priceColor}
454
- buttonColor={colors.kuposButtonColor}
455
- boardingIcon={renderIcon("whiteBoardingIcon", "14px")}
456
- getAnimationIcon={getAnimationIcon}
457
- countdownSeconds={countdownSeconds}
458
- onCountdownEnd={() => {
459
- const cardEl = document.getElementById(
460
- `service-card-${serviceItem.id}`,
461
- );
462
- if (!cardEl) return;
463
- cardEl.style.border = "1px solid #ccc";
464
- if (!showTopLabel) {
465
- cardEl.style.borderRadius = "10px";
466
- }
467
- }}
468
- offerText={serviceItem?.offer_text}
469
- /> */}
470
391
  <div
471
392
  id={`service-card-${serviceItem.id}`}
472
- className="bg-white mx-auto relative rounded-[10px] border border-[#ccc]"
473
- // style={{
474
- // border:
475
- // countdownSeconds > 0 && serviceItem?.offer_text
476
- // ? `1px solid ${colors.priceColor}`
477
- // : "1px solid #ccc",
478
- // borderRadius: showTopLabel
479
- // ? "10px 0 10px 10px"
480
- // : countdownSeconds > 0 && serviceItem?.offer_text
481
- // ? "10px 0 10px 10px"
482
- // : "10px",
483
- // }}
393
+ className={`bg-white mx-auto relative ${
394
+ hasOfferText
395
+ ? "rounded-[18px]"
396
+ : "rounded-[10px] border border-[#ccc]"
397
+ }`}
398
+ style={serviceCardStyle}
484
399
  >
485
400
  <div
486
401
  className=" pt-[20px]"
@@ -488,28 +403,19 @@ function ServiceItemPB({
488
403
  padding: coachKey
489
404
  ? "15px 15px 20px 15px"
490
405
  : "20px 15px 11px 15px",
406
+ marginTop: hasOfferText ? "14px" : "0",
491
407
  }}
492
408
  >
493
409
  <div
494
- // className="grid text-[#464647] w-full [rid-template-columns:18%_28%_2.5%_28%_15.5%] gap-x-[2%] items-center"
495
410
  className="grid text-[#464647] w-full [grid-template-columns:22%_28%_2.5%_24%_15.5%] gap-x-[2%] items-center"
496
- style={{
497
- marginTop:
498
- showTopLabel || serviceItem?.is_direct_trip ? "8px" : "",
499
- }}
411
+ // style={{
412
+ // marginTop:
413
+ // showTopLabel || serviceItem?.is_direct_trip ? "8px" : "",
414
+ // }}
500
415
  >
501
416
  {/* OPERATOR LOGO */}
502
- <div
503
- style={{
504
- display: "flex",
505
- flexDirection: "column",
506
- gap: "5px",
507
- }}
508
- >
509
- <div
510
- // className="flex items-center justify-center m-[auto]"
511
- className=""
512
- >
417
+ <div className="flex flex-col gap-[5px]">
418
+ <div>
513
419
  <img
514
420
  src={serviceItem.operator_details[0]}
515
421
  alt="service logo"
@@ -565,13 +471,14 @@ function ServiceItemPB({
565
471
  currencySign={currencySign}
566
472
  removeDuplicateSeats={removeDuplicateSeats}
567
473
  isPeru={isPeru}
474
+ renderIcon={renderIcon}
568
475
  />
569
476
  </div>
570
477
 
571
478
  {/* BUTTON */}
572
479
 
573
480
  <div className="relative">
574
- {showLastSeats ? (
481
+ {/* {showLastSeats ? (
575
482
  <div
576
483
  className="flex justify-end mr-[11px] w-[100%] right-[0px] absolute"
577
484
  style={{
@@ -585,7 +492,7 @@ function ServiceItemPB({
585
492
  </div>
586
493
  )}
587
494
  </div>
588
- ) : null}
495
+ ) : null} */}
589
496
  <KuposButton
590
497
  isSoldOut={isSoldOut}
591
498
  isLoading={serviceDetailsLoading}
@@ -595,6 +502,21 @@ function ServiceItemPB({
595
502
  soldOutIcon={renderIcon("soldOutIcon", "14px")}
596
503
  onClick={checkMidnight}
597
504
  />
505
+ {showLastSeats ? (
506
+ <div
507
+ className="flex justify-center mr-[11px] w-[100%] right-[0px]"
508
+ style={{
509
+ top: serviceDetailsLoading ? "-17px" : "-20px",
510
+ }}
511
+ >
512
+ {serviceItem?.available_seats < 10 &&
513
+ serviceItem?.available_seats > 0 && (
514
+ <div className="text-[12px] text-[#464647] mt-1 text-center">
515
+ ¡Últimos Asientos!
516
+ </div>
517
+ )}
518
+ </div>
519
+ ) : null}
598
520
  </div>
599
521
  </div>
600
522
  <BottomAmenities
@@ -630,7 +552,19 @@ function ServiceItemPB({
630
552
  }}
631
553
  >
632
554
  <div
633
- style={{ overflow: "hidden", minHeight: 0, marginTop: "-10px" }}
555
+ style={{
556
+ overflow: "hidden",
557
+ minHeight: 0,
558
+ marginTop: "-10px",
559
+ ...(hasOfferText
560
+ ? {
561
+ borderLeft: "6px solid #ff5964",
562
+ borderRight: "6px solid #ff8842",
563
+ borderRadius: "0 0 18px 18px",
564
+ boxSizing: "border-box",
565
+ }
566
+ : {}),
567
+ }}
634
568
  >
635
569
  <ExpandedDropdown
636
570
  serviceItem={serviceItem}
@@ -649,35 +583,10 @@ function ServiceItemPB({
649
583
  <div
650
584
  className="text-white p-[10px_15px] text-left w-full flex items-center absolute -bottom-[36px] pt-[50px] -z-10 rounded-b-[14px] text-[14px]"
651
585
  style={{
652
- backgroundColor: colors?.bottomStripColor,
586
+ background: offerGradient,
653
587
  opacity: isSoldOut ? 0.5 : 1,
654
588
  }}
655
589
  >
656
- {/* <div className="flex justify-between items-center w-full">
657
- <div className="flex items-center">
658
- <LottiePlayer
659
- animationData={getAnimationIcon("bombAnimation")}
660
- width="18px"
661
- height="18px"
662
- />
663
- <span className="bold-text ml-[8px]">
664
- {" "}
665
- {serviceItem?.offer_text || ""}
666
- </span>
667
- </div>
668
- <div>
669
- Termina en&nbsp;
670
- <span
671
- className="bold-text text-end"
672
- ref={startCountdown}
673
- style={{
674
- fontVariantNumeric: "tabular-nums",
675
- display: "inline-block",
676
- // minWidth: "70px",
677
- }}
678
- />
679
- </div>
680
- </div> */}
681
590
  <div className="flex justify-between items-center w-full">
682
591
  <div className="flex items-center ">
683
592
  <div className="flex items-center">
@@ -693,7 +602,7 @@ function ServiceItemPB({
693
602
  | Termina en&nbsp;
694
603
  <span
695
604
  className="bold-text text-end"
696
- ref={startCountdown}
605
+ ref={(node) => CommonService.startCountdown(node, 599)}
697
606
  style={{
698
607
  fontVariantNumeric: "tabular-nums",
699
608
  display: "inline-block",
@@ -704,101 +613,50 @@ function ServiceItemPB({
704
613
  </div>
705
614
  </div>
706
615
  <div className="flex items-center">
707
- {renderIcon("personIcon", "16px")}
708
- &nbsp;
616
+ {/* {renderIcon("personIcon", "16px")} */}
617
+ <LottiePlayer
618
+ animationData={getAnimationIcon("dotAnimation")}
619
+ width="12px"
620
+ height="12px"
621
+ />
622
+
709
623
  <span className="ml-[6px]">
710
624
  <span
711
625
  className="bold-text"
712
- ref={startViewerCount}
626
+ ref={(node) =>
627
+ CommonService.startViewerCount(node, viewersConfig)
628
+ }
713
629
  style={{ fontVariantNumeric: "tabular-nums" }}
714
630
  />{" "}
715
631
  <span className="bold-text">personas</span>{" "}
716
632
  <span>
717
633
  {" "}
718
- {viewersConfig?.label || " están viendo este viaje"}
634
+ {viewersConfig?.label || " viendo"} |{" "}
635
+ <span className="">
636
+ ⚡ Quedan pocos{" "}
637
+ <span
638
+ className="bold-text"
639
+ ref={(node) =>
640
+ CommonService.startComprandoCount(node, 4, 16)
641
+ }
642
+ style={{ fontVariantNumeric: "tabular-nums" }}
643
+ />{" "}
644
+ comprando
645
+ </span>
719
646
  </span>
720
647
  </span>
721
648
  </div>
722
649
  </div>
723
650
  </div>
724
651
  )}
725
- <div className="absolute -top-[11px] left-0 w-full flex items-center justify-end gap-[12px] pr-[15px] z-10 ">
726
- {showTopLabel && (
727
- <div
728
- className={`flex items-center gap-[10px] py-[4px] px-[14px] rounded-[38px] text-[12.5px] z-10`}
729
- style={{
730
- backgroundColor: isSoldOut
731
- ? "#ddd"
732
- : colors.ratingBottomColor,
733
- }}
734
- >
735
- <div className={isSoldOut ? "grayscale" : ""}>
736
- <LottiePlayer
737
- // animationData={serviceItem.icons.priorityStageAnim}
738
- animationData={getAnimationIcon("priorityStageAnim")}
739
- width="14px"
740
- height="14px"
741
- />
742
- </div>
743
- <div
744
- className={
745
- isSoldOut ? "text-white" : `text-[${colors.topLabelColor}]`
746
- }
747
- >
748
- {showTopLabel}
749
- </div>
750
- </div>
751
- )}
752
- {serviceItem?.is_transpordo && (
753
- <div
754
- className={`flex items-center gap-[10px] py-[4px] text-white px-[14px] rounded-[38px] text-[12.5px] z-20`}
755
- style={{
756
- backgroundColor: isSoldOut ? "#ddd" : colors.tooltipColor,
757
- }}
758
- >
759
- {renderIcon("connectingServiceIcon", "12px")}
760
- {/* <LottiePlayer
761
- animationData={serviceItem.icons.connectingServiceIcon}
762
- // animationData={getAnimationIcon(connectingServiceIcon)}
763
- width="14px"
764
- height="14px"
765
- /> */}
766
- <div>{"Conexión"}</div>
767
- </div>
768
- )}
769
- {serviceItem?.is_direct_trip && (
770
- <div
771
- className={`flex items-center gap-[10px] py-[4px] text-white px-[14px] rounded-[38px] text-[12.5px] z-20 `}
772
- style={{
773
- backgroundColor: isSoldOut ? "#ddd" : colors.tooltipColor,
774
- }}
775
- >
776
- <LottiePlayer
777
- // animationData={serviceItem.icons.directoAnim}
778
- animationData={getAnimationIcon("directoAnim")}
779
- width="14px"
780
- height="14px"
781
- />
782
- <div>{translation?.directService}</div>
783
- </div>
784
- )}
785
- {serviceItem?.train_type_label === "Tren Express (Nuevo)" && (
786
- <div
787
- className={`flex items-center gap-[10px] py-[4px] text-white px-[14px] rounded-[38px] text-[12.5px] z-20 `}
788
- style={{
789
- backgroundColor: isSoldOut ? "#ddd" : colors.tooltipColor,
790
- }}
791
- >
792
- <LottiePlayer
793
- // animationData={serviceItem.icons.directoAnim}
794
- animationData={getAnimationIcon("directoAnim")}
795
- width="14px"
796
- height="14px"
797
- />
798
- <div>{"Tren Express"}</div>
799
- </div>
800
- )}
801
- </div>
652
+ <ServiceBadges
653
+ showTopLabel={showTopLabel}
654
+ isSoldOut={isSoldOut}
655
+ colors={colors}
656
+ renderIcon={renderIcon}
657
+ translation={translation}
658
+ serviceItem={serviceItem}
659
+ />
802
660
  </div>
803
661
  )}
804
662
  </>