kupos-ui-components-lib 9.9.7 → 9.9.8

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 (42) hide show
  1. package/dist/KuposUIComponent.d.ts +3 -0
  2. package/dist/components/ServiceItem/PeruServiceItemDesktop.d.ts +1 -1
  3. package/dist/components/ServiceItem/PeruServiceItemDesktop.js +156 -176
  4. package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
  5. package/dist/components/ServiceItem/ServiceItemDesktop.js +29 -31
  6. package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
  7. package/dist/components/ServiceItem/ServiceItemMobile.js +43 -17
  8. package/dist/components/ServiceItem/mobileTypes.d.ts +48 -2
  9. package/dist/components/ServiceItem/types.d.ts +27 -8
  10. package/dist/styles.css +219 -16
  11. package/dist/ui/ExpendedDropDown/ExpandedDropdown.d.ts +1 -2
  12. package/dist/ui/ExpendedDropDown/ExpandedDropdown.js +2 -4
  13. package/dist/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.js +3 -10
  14. package/dist/ui/OfferBanner.d.ts +2 -0
  15. package/dist/ui/OfferBanner.js +22 -15
  16. package/dist/ui/SeatSection/SeatSection.d.ts +1 -7
  17. package/dist/ui/SeatSection/SeatSection.js +12 -41
  18. package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +1 -2
  19. package/dist/ui/mobileweb/DateTimeSectionMobile.js +6 -12
  20. package/dist/ui/mobileweb/SeatSectionMobile.d.ts +1 -2
  21. package/dist/ui/mobileweb/SeatSectionMobile.js +14 -21
  22. package/dist/utils/CommonService.d.ts +4 -1
  23. package/dist/utils/CommonService.js +19 -6
  24. package/package.json +1 -1
  25. package/src/KuposUIComponent.tsx +3 -0
  26. package/src/assets/images/anims/service_list/flame_anim.json +1 -0
  27. package/src/assets/images/anims/service_list/thunder_icon.json +1 -0
  28. package/src/assets/images/anims/service_list/users_anim.json +1 -0
  29. package/src/components/ServiceItem/PeruServiceItemDesktop.tsx +404 -277
  30. package/src/components/ServiceItem/ServiceItemDesktop.tsx +71 -51
  31. package/src/components/ServiceItem/ServiceItemMobile.tsx +387 -290
  32. package/src/components/ServiceItem/mobileTypes.ts +50 -8
  33. package/src/components/ServiceItem/types.ts +32 -13
  34. package/src/styles.css +15 -0
  35. package/src/ui/ExpendedDropDown/ExpandedDropdown.tsx +2 -4
  36. package/src/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.tsx +575 -0
  37. package/src/ui/FeatureServiceUI/FeatureServiceUi.tsx +634 -0
  38. package/src/ui/OfferBanner.tsx +71 -43
  39. package/src/ui/SeatSection/SeatSection.tsx +21 -86
  40. package/src/ui/mobileweb/DateTimeSectionMobile.tsx +35 -44
  41. package/src/ui/mobileweb/SeatSectionMobile.tsx +11 -23
  42. package/src/utils/CommonService.ts +27 -8
@@ -18,31 +18,65 @@ import pullmanFlexibleAnimation from "../../assets/images/anims/service_list/pul
18
18
  import pullmanPetFriendlyAnimation from "../../assets/images/anims/service_list/pullmanPetFriendly.json";
19
19
  import pullmanLocationAnimation from "../../assets/images/anims/service_list/pullmanLocation.json";
20
20
  import pullmanPriorityStageAnimation from "../../assets/images/anims/service_list/pullmanPriorityStage.json";
21
- import pullmanPromoAnimation from "../../assets/images/anims/service_list/promocion.json";
22
- import pullmanDirectoAnimation from "../../assets/images/anims/service_list/directo.json";
23
21
 
24
22
  import opsitesFlexibleAnimation from "../../assets/images/anims/service_list/opsitesFlexible.json";
25
23
  import opsitesPetFriendlyAnimation from "../../assets/images/anims/service_list/opsitesPetFriendly.json";
26
24
  import opsitesLocationAnimation from "../../assets/images/anims/service_list/opsitesLocation.json";
27
25
  import opsitesPriorityStageAnimation from "../../assets/images/anims/service_list/opsitesPriorityStage.json";
28
- import opsitesPromoAnimation from "../../assets/images/anims/service_list/promocion.json";
29
- import opsitesDirectoAnimation from "../../assets/images/anims/service_list/directo.json";
30
-
31
- import linatalFlexibleAnimation from "../../assets/images/anims/service_list/flexible.json";
32
- import linatalPromoAnimation from "../../assets/images/anims/service_list/promocion.json";
33
- import linatalDirectoAnimation from "../../assets/images/anims/service_list/directo.json";
34
- import linatalPriorityStageAnimation from "../../assets/images/anims/service_list/priority_stage.json";
35
- import linatalPetFriendlyAnimation from "../../assets/images/anims/service_list/pet_friendly.json";
36
- import linatalLocationAnimation from "../../assets/images/anims/service_list/location.json";
26
+
27
+ import bombAnimation from "../../assets/images/anims/service_list/bomb.json";
28
+ import dotAnimation from "../../assets/images/anims/service_list/dot_animation.json";
29
+
37
30
  import StageTooltip from "../../ui/StagesTooltip";
38
31
  import RatingBlock from "../../ui/RatingBlock";
39
32
  import DurationBlock from "../../ui/DurationBlock";
40
33
  import PetBlock from "../../ui/PetBlock";
41
34
  import FlexibleBlock from "../../ui/FlexibleBlock";
42
35
  import AmenitiesBlock from "../../ui/AmenitiesBlock";
36
+ import SeatSection from "../../ui/SeatSection/SeatSection";
37
+ import KuposButton from "../../ui/KuposButton/KuposButton";
38
+ import BottomAmenities from "../../ui/BottomAmenities/BottomAmenities";
39
+ import OfferBanner from "../../ui/OfferBanner";
40
+ import ServiceBadges from "../../ui/ServiceBadges/ServiceBadges";
43
41
 
44
42
  const SEAT_EXCEPTIONS = ["Asiento mascota"];
45
43
 
44
+ const ANIMATION_MAP: Record<string, Record<string, any>> = {
45
+ promoAnim: {
46
+ kupos: promoAnimation,
47
+ },
48
+ locationAnim: {
49
+ kupos: locationAnimation,
50
+ pullman: pullmanLocationAnimation,
51
+ opsites: opsitesLocationAnimation,
52
+ },
53
+ directoAnim: {
54
+ kupos: directoAnimation,
55
+ },
56
+ petFriendlyAnim: {
57
+ kupos: petFriendlyAnimation,
58
+ pullman: pullmanPetFriendlyAnimation,
59
+ opsites: opsitesPetFriendlyAnimation,
60
+ },
61
+ priorityStageAnim: {
62
+ kupos: priorityStageAnimation,
63
+ pullman: pullmanPriorityStageAnimation,
64
+ opsites: opsitesPriorityStageAnimation,
65
+ },
66
+ flexibleIcon: {
67
+ kupos: flexibleAnimation,
68
+ pullman: pullmanFlexibleAnimation,
69
+ opsites: opsitesFlexibleAnimation,
70
+ },
71
+ bombAnimation: {
72
+ kupos: bombAnimation,
73
+ },
74
+ dotAnimation: {
75
+ kupos: dotAnimation,
76
+ opsites: dotAnimation,
77
+ },
78
+ };
79
+
46
80
  function PeruServiceItemDesktop({
47
81
  serviceItem,
48
82
  onBookButtonPress,
@@ -67,53 +101,21 @@ function PeruServiceItemDesktop({
67
101
  isPeru,
68
102
  siteType,
69
103
  isAllinBus,
104
+ viewersConfig,
105
+ isExpand,
106
+ setIsExpand,
107
+ coachKey,
108
+ isLoggedIn,
109
+ showLoginModal,
70
110
  isFlores,
111
+ isNewUiEnabled,
112
+ showLoginOption,
71
113
  t = (key: string) => key,
72
114
  }: ServiceItemProps & { currencySign?: string }): React.ReactElement {
73
- const animationMap: Record<string, Record<string, any>> = {
74
- promoAnim: {
75
- kupos: promoAnimation,
76
- pullman: pullmanPromoAnimation,
77
- opsites: opsitesPromoAnimation,
78
- linatal: linatalPromoAnimation,
79
- },
80
- locationAnim: {
81
- kupos: locationAnimation,
82
- pullman: pullmanLocationAnimation,
83
- opsites: opsitesLocationAnimation,
84
- linatal: linatalLocationAnimation,
85
- },
86
- directoAnim: {
87
- kupos: directoAnimation,
88
- pullman: pullmanDirectoAnimation,
89
- opsites: opsitesDirectoAnimation,
90
- linatal: linatalDirectoAnimation,
91
- },
92
- petFriendlyAnim: {
93
- kupos: petFriendlyAnimation,
94
- pullman: pullmanPetFriendlyAnimation,
95
- opsites: opsitesPetFriendlyAnimation,
96
- linatal: linatalPetFriendlyAnimation,
97
- },
98
- priorityStageAnim: {
99
- kupos: priorityStageAnimation,
100
- pullman: pullmanPriorityStageAnimation,
101
- opsites: opsitesPriorityStageAnimation,
102
- linatal: linatalPriorityStageAnimation,
103
- },
104
- flexibleIcon: {
105
- kupos: flexibleAnimation,
106
- pullman: pullmanFlexibleAnimation,
107
- opsites: opsitesFlexibleAnimation,
108
- linatal: linatalFlexibleAnimation,
109
- },
110
- };
111
-
112
115
  const getAnimationIcon = (icon: string) => {
113
- const animation = animationMap[icon];
116
+ const animation = ANIMATION_MAP[icon];
114
117
  if (!animation) return null;
115
- const currentSiteType = siteType || "kupos";
116
- return animation[currentSiteType];
118
+ return animation[siteType] ?? animation.kupos;
117
119
  };
118
120
  const SvgAmenities = ({
119
121
  moreAnemities,
@@ -149,8 +151,9 @@ function PeruServiceItemDesktop({
149
151
  style={{
150
152
  filter: color === "white" ? "brightness(0) invert(1)" : "",
151
153
  }}
152
- className={`object-contain ${moreAnemities ? "w-[16px] h-[16px]" : "w-[16px] h-[16px]"
153
- }`}
154
+ className={`object-contain ${
155
+ moreAnemities ? "w-[16px] h-[16px]" : "w-[16px] h-[16px]"
156
+ }`}
154
157
  />
155
158
  );
156
159
  };
@@ -167,6 +170,7 @@ function PeruServiceItemDesktop({
167
170
  busStage[labelId].split("|")[0];
168
171
 
169
172
  let isSoldOut = serviceItem.available_seats <= 0;
173
+ const hasDpEnabled = serviceItem?.is_dp_enabled === true;
170
174
 
171
175
  const renderIcon = (iconKey: string, size: string = "14px") => {
172
176
  const iconValue = serviceItem.icons?.[iconKey];
@@ -261,8 +265,9 @@ function PeruServiceItemDesktop({
261
265
  SEAT_EXCEPTIONS.includes(val.label) ? null : (
262
266
  <span
263
267
  key={key}
264
- className={`flex items-center justify-between text-[13.33px] ${isSoldOut ? "text-[#c0c0c0]" : ""
265
- }`}
268
+ className={`flex items-center justify-between text-[13.33px] ${
269
+ isSoldOut ? "text-[#c0c0c0]" : ""
270
+ }`}
266
271
  >
267
272
  <div>
268
273
  <img
@@ -287,8 +292,9 @@ function PeruServiceItemDesktop({
287
292
  SEAT_EXCEPTIONS.includes(val.label) ? null : (
288
293
  <span
289
294
  key={key}
290
- className={`flex items-center justify-between text-[13.33px] ${isSoldOut ? "text-[#c0c0c0]" : ""
291
- }`}
295
+ className={`flex items-center justify-between text-[13.33px] ${
296
+ isSoldOut ? "text-[#c0c0c0]" : ""
297
+ }`}
292
298
  >
293
299
  <div>
294
300
  <img
@@ -344,6 +350,8 @@ function PeruServiceItemDesktop({
344
350
  };
345
351
 
346
352
  const checkMidnight = () => {
353
+ setIsExpand?.(null);
354
+
347
355
  if (
348
356
  cityOrigin?.label &&
349
357
  cityDestination?.label &&
@@ -448,26 +456,56 @@ function PeruServiceItemDesktop({
448
456
  ? extractStage(serviceItem.stage_details_arr, 1)
449
457
  : null;
450
458
 
459
+ const countdownSeconds = 599;
460
+
461
+ const startCountdown = (node: HTMLSpanElement | null) => {
462
+ if (!node) return;
463
+
464
+ const prevId = node.dataset.countdownId;
465
+ if (prevId) clearInterval(Number(prevId));
466
+
467
+ let remaining = countdownSeconds;
468
+
469
+ const formatTime = (totalSecs: number) => {
470
+ const m = Math.floor(totalSecs / 60);
471
+ const s = totalSecs % 60;
472
+ return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
473
+ };
474
+
475
+ node.textContent = formatTime(remaining);
476
+
477
+ const id = setInterval(() => {
478
+ remaining -= 1;
479
+ if (remaining <= 0) {
480
+ remaining = countdownSeconds;
481
+ }
482
+ node.textContent = formatTime(remaining);
483
+ }, 1000);
484
+
485
+ node.dataset.countdownId = String(id);
486
+ };
487
+
451
488
  const items = [
452
489
  {
453
- key: "rating",
454
- width: "30%",
490
+ key: "amenities",
491
+ width: "20%",
492
+ condition: true,
455
493
  render: (
456
- <RatingBlock
457
- showRating={showRating}
494
+ <AmenitiesBlock
458
495
  serviceItem={serviceItem}
496
+ metaData={metaData}
459
497
  isSoldOut={isSoldOut}
460
498
  colors={colors}
461
- t={t}
462
- translation={translation}
499
+ getAnimationIcon={getAnimationIcon}
500
+ getAmenityName={CommonService.getAmenityName}
501
+ SvgAmenities={SvgAmenities}
463
502
  isPeru={isPeru}
464
503
  />
465
504
  ),
466
505
  },
467
-
468
506
  {
469
507
  key: "duration",
470
- width: "20%",
508
+ width: "12%",
471
509
  condition: serviceItem.duration,
472
510
  render: (
473
511
  <DurationBlock
@@ -480,6 +518,20 @@ function PeruServiceItemDesktop({
480
518
  ),
481
519
  },
482
520
 
521
+ // {
522
+ // key: "directo",
523
+ // width: "12%",
524
+ // condition: serviceItem?.is_direct_trip === true,
525
+ // render: (
526
+ // <DirectoBlock
527
+ // translation={translation}
528
+ // getAnimationIcon={getAnimationIcon}
529
+ // colors={colors}
530
+ // isSoldOut={isSoldOut}
531
+ // />
532
+ // ),
533
+ // },
534
+
483
535
  {
484
536
  key: "pet",
485
537
  width: "20%",
@@ -499,7 +551,7 @@ function PeruServiceItemDesktop({
499
551
  {
500
552
  key: "flexible",
501
553
  width: "20%",
502
- condition: false,
554
+ condition: serviceItem.is_change_ticket === true,
503
555
  render: (
504
556
  <FlexibleBlock
505
557
  translation={translation}
@@ -510,81 +562,145 @@ function PeruServiceItemDesktop({
510
562
  />
511
563
  ),
512
564
  },
513
-
514
- {
515
- key: "amenities",
516
- width: "20%",
517
- render: (
518
- <AmenitiesBlock
519
- serviceItem={serviceItem}
520
- metaData={metaData}
521
- isSoldOut={isSoldOut}
522
- colors={colors}
523
- isPeru={isPeru}
524
- getAnimationIcon={getAnimationIcon}
525
- getAmenityName={CommonService.getAmenityName}
526
- SvgAmenities={SvgAmenities}
527
- />
528
- ),
529
- },
530
565
  ];
531
566
 
532
- const amenitiesItem = items.find((i) => i.key === "amenities");
533
567
  const otherItems = items.filter(
534
- (i) => i.key !== "amenities" && i.condition !== false,
568
+ (i) => i.key !== "pet" && i.key !== "flexible" && !!i.condition,
569
+ );
570
+
571
+ const isItemExpanded = serviceItem.id === isExpand || isExpand === true;
572
+ const grayscaleClass = isSoldOut ? "grayscale" : "";
573
+
574
+ const hasOfferText = Boolean(serviceItem?.offer_text);
575
+
576
+ const offerGradient = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"} 0%, ${colors.leftGradiantColor || "#ff8842"} 100%)`;
577
+ const offerGradientWithOpacity = `linear-gradient(90deg, ${colors.rightGradiantColor || "#ff5964"}80 0%, ${colors.leftGradiantColor || "#ff8842"}80 100%)`;
578
+ const serviceCardStyle: React.CSSProperties =
579
+ (hasOfferText && isNewUiEnabled) || hasDpEnabled
580
+ ? {
581
+ borderColor: "transparent",
582
+ borderStyle: "solid",
583
+ borderWidth: "3px 3px 0 3px",
584
+ borderRadius: isItemExpanded || coachKey ? "18px 18px 0 0" : "18px",
585
+ background: `linear-gradient(#fff, #fff) padding-box, ${isSoldOut ? offerGradientWithOpacity : offerGradient} border-box`,
586
+ // zIndex: 1,
587
+ }
588
+ : {};
589
+
590
+ const seats = removeDuplicateSeats
591
+ ? serviceItem.seat_types?.filter(
592
+ (seat, index, self) =>
593
+ index === self.findIndex((s) => s.label === seat.label),
594
+ ) || []
595
+ : serviceItem.seat_types || [];
596
+
597
+ const discountedSeats = seats.map((seat) => ({
598
+ ...seat,
599
+ ...CommonService.calculateDiscountedPrice(seat.fare, serviceItem as any),
600
+ }));
601
+
602
+ const hasDiscount = discountedSeats.some(
603
+ (seat) => seat.originalPrice !== seat.discountedPrice,
535
604
  );
536
605
 
537
606
  return (
538
607
  <div
539
- className={`relative ${serviceItem.offer_text ? "mb-[55px]" : "mb-[10px]"
540
- } ${serviceItem?.is_direct_trip ||
541
- serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
542
- showTopLabel
608
+ className={`relative hover:z-[150] ${hasOfferText ? "mb-[55px]" : "mb-[10px]"} ${
609
+ serviceItem?.is_direct_trip ||
610
+ serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
611
+ showTopLabel
543
612
  ? "mt-[24px]"
544
613
  : "mt-[20px]"
545
- } `}
614
+ } `}
546
615
  >
616
+ {(serviceItem?.offer_text || hasDpEnabled) && !isSoldOut && (
617
+ <OfferBanner
618
+ offerGradient={offerGradient}
619
+ isSoldOut={isSoldOut}
620
+ serviceItem={serviceItem}
621
+ renderIcon={renderIcon}
622
+ isLoggedIn={isLoggedIn}
623
+ showLoginModal={showLoginModal}
624
+ viewersConfig={viewersConfig}
625
+ getAnimationIcon={getAnimationIcon}
626
+ showLoginOption={showLoginOption}
627
+ isNewUiEnabled={isNewUiEnabled}
628
+ colors={colors}
629
+ />
630
+ )}
547
631
  <div
548
- className={"bg-white rounded-[20px] shadow-service mx-auto relative"}
632
+ id={`service-card-${serviceItem.id}`}
633
+ className={`bg-white mx-auto relative ${
634
+ (hasOfferText && isNewUiEnabled && !isSoldOut) || hasDpEnabled
635
+ ? "z-[3] rounded-[18px]"
636
+ : "rounded-[10px] border border-[#ccc]"
637
+ }`}
638
+ style={serviceCardStyle}
549
639
  >
550
- <div className="p-[15px] pt-[20px]">
551
- {/* Header with operator info and favorite */}
552
- {/* <div className="flex justify-between items-center mb-[15px]">
553
- <div className="flex items-center justify-between w-[250px]">
554
- <div className="w-[120px] overflow-y-hidden">
640
+ <div
641
+ className=" pt-[20px]"
642
+ style={{
643
+ padding: hasOfferText
644
+ ? "20px 15px 10px 15px"
645
+ : coachKey
646
+ ? "20px 15px 20px 15px"
647
+ : "20px 15px 10px 15px",
648
+
649
+ marginTop: hasDiscount || hasOfferText ? "14px" : "",
650
+ }}
651
+ >
652
+ {/* <div className="grid grid-cols-[1.5fr_1fr_auto] gap-[3rem] sm:gap-[4rem] md:gap-[5rem] lg:gap-[6rem] xl:gap-[5rem] 2xl:gap-[7rem] text-[#464647]"> */}
653
+ <div
654
+ className="grid text-[#464647] w-full [grid-template-columns:14%_40%_0.5%_24%_13.5%] gap-x-[2%] items-center"
655
+ // style={{ marginTop: showTopLabel ? "8px" : "" }}
656
+ style={{
657
+ marginTop:
658
+ showTopLabel || serviceItem?.is_direct_trip ? "8px" : "",
659
+ }}
660
+ >
661
+ {/* OPERATOR LOGO */}
662
+ <div
663
+ style={{
664
+ display: "flex",
665
+ flexDirection: "column",
666
+ // gap: "5px",
667
+ }}
668
+ >
669
+ <div
670
+ // className="flex items-center justify-center m-[auto]"
671
+ className=""
672
+ >
555
673
  <img
556
674
  src={serviceItem.operator_details[0]}
557
675
  alt="service logo"
558
- className={`w-[120px] h-auto object-contain ${
559
- isSoldOut ? "grayscale" : ""
560
- }`}
676
+ className={`h-auto object-contain ${
677
+ isFlores ? "w-[80%]" : "w-full"
678
+ } ${isSoldOut ? "grayscale" : ""}`}
561
679
  />
562
- </div>
563
-
564
- <div>
565
- {" "}
566
680
  {isCiva ? (
567
- <div className="text-[13.33px] black-text">
681
+ <div className="text-[13.33px] black-text ml-2">
568
682
  {serviceItem.operator_details[2]}
569
683
  </div>
570
684
  ) : null}
571
685
  </div>
686
+ <RatingBlock
687
+ showRating={showRating}
688
+ serviceItem={serviceItem}
689
+ isSoldOut={isSoldOut}
690
+ colors={colors}
691
+ t={t}
692
+ translation={translation}
693
+ isPeru={isPeru}
694
+ />
572
695
  </div>
573
- </div> */}
574
-
575
- {/* <div className="grid grid-cols-[1.5fr_1fr_auto] gap-[3rem] sm:gap-[4rem] md:gap-[5rem] lg:gap-[6rem] xl:gap-[5rem] 2xl:gap-[7rem] text-[#464647]"> */}
576
- <div
577
- className="grid text-[#464647] w-full [grid-template-columns:14%_40%_0.5%_24%_13.5%] gap-x-[2%] items-center"
578
- style={{ marginTop: showTopLabel ? "8px" : "" }}
579
- >
580
- {/* OPERATOR LOGO */}
581
- <div className="flex items-center justify-center m-[auto]">
696
+ {/* <div className="flex items-center justify-center m-[auto]">
582
697
  <div className=" ">
583
698
  <img
584
699
  src={serviceItem.operator_details[0]}
585
700
  alt="service logo"
586
- className={`h-auto object-contain ${isFlores ? "w-[80%]" : "w-full"
587
- } ${isSoldOut ? "grayscale" : ""}`}
701
+ className={` h-auto object-contain ${
702
+ isSoldOut ? "grayscale" : ""
703
+ }`}
588
704
  />
589
705
  </div>
590
706
  {isCiva ? (
@@ -592,18 +708,19 @@ function PeruServiceItemDesktop({
592
708
  {serviceItem.operator_details[2]}
593
709
  </div>
594
710
  ) : null}
595
- </div>
711
+ </div> */}
596
712
 
597
713
  {/* DATE AND TIME - Grid Layout */}
598
714
  <div
599
- className={`min-h-[2.5rem] grid grid-cols-[0.8fr_auto_26%_1fr] gap-x-4 items-center text-[13.33px] ${isSoldOut ? "text-[#c0c0c0]" : ""
600
- }`}
715
+ className={`min-h-[2.5rem] grid grid-cols-[0.8fr_auto_26%_1fr] gap-x-4 items-center text-[13.33px] ${
716
+ isSoldOut ? "text-[#c0c0c0]" : ""
717
+ }`}
601
718
  style={{
602
719
  gridTemplateRows: "1fr",
603
720
  }}
604
721
  >
605
722
  {/* ICONS COLUMN */}
606
- <div className="flex flex-col gap-[10px]">
723
+ <div className="flex flex-col gap-[4px]">
607
724
  {/* Origin Icon */}
608
725
  {orignLabel ? (
609
726
  <div className="w-[60px] h-[20px] flex items-center bold-text">
@@ -614,8 +731,9 @@ function PeruServiceItemDesktop({
614
731
  <img
615
732
  src={serviceItem.icons?.origin}
616
733
  alt="origin"
617
- className={`w-[18px] h-auto mr-[8px] ${isSoldOut ? "grayscale" : ""
618
- }`}
734
+ className={`w-[18px] h-auto mr-[8px] ${
735
+ isSoldOut ? "grayscale" : ""
736
+ }`}
619
737
  />
620
738
  </div>
621
739
  )}
@@ -630,8 +748,9 @@ function PeruServiceItemDesktop({
630
748
  <div className="h-[20px] flex items-center">
631
749
  <img
632
750
  src={serviceItem.icons?.destination}
633
- className={`w-[18px] h-auto mr-[8px] ${isSoldOut ? "grayscale" : ""
634
- }`}
751
+ className={`w-[18px] h-auto mr-[8px] ${
752
+ isSoldOut ? "grayscale" : ""
753
+ }`}
635
754
  style={{ opacity: isSoldOut ? 0.5 : 1 }}
636
755
  />
637
756
  </div>
@@ -639,7 +758,7 @@ function PeruServiceItemDesktop({
639
758
  </div>
640
759
 
641
760
  {/* DATES COLUMN */}
642
- <div className="flex flex-col gap-[10px]">
761
+ <div className="flex flex-col gap-[4px]">
643
762
  {/* Departure Date */}
644
763
  <StageTooltip
645
764
  stageData={serviceItem.boarding_stages}
@@ -672,7 +791,7 @@ function PeruServiceItemDesktop({
672
791
  </div>
673
792
 
674
793
  {/* DOTS COLUMN */}
675
- <div className="flex flex-col gap-[10px] items-center">
794
+ <div className="flex flex-col gap-[4px] items-center">
676
795
  {/* Departure Dot */}
677
796
  <div className="h-[20px] flex items-center justify-center">
678
797
  <div>•</div>
@@ -689,7 +808,7 @@ function PeruServiceItemDesktop({
689
808
  </div>
690
809
 
691
810
  {/* TIMES COLUMN */}
692
- <div className="flex flex-col gap-[10px]">
811
+ <div className="flex flex-col gap-[4px]">
693
812
  {/* Departure Time */}
694
813
  <StageTooltip
695
814
  stageData={serviceItem.boarding_stages}
@@ -735,142 +854,180 @@ function PeruServiceItemDesktop({
735
854
  ></div>
736
855
  {/* SEATS */}
737
856
  <div className="content-center">
738
- <div
739
- className={`relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.5rem] ${getNumberOfSeats() < 3 ? "" : ""
740
- }`}
741
- style={
742
- getNumberOfSeats() < 2
743
- ? { alignItems: "center" }
744
- : { alignItems: "center" }
745
- }
746
- >
747
- <div className="flex flex-col justify-between">
748
- {getSeatNames()}
749
- </div>
857
+ <SeatSection
858
+ seatTypes={serviceItem.seat_types}
859
+ serviceItem={serviceItem}
860
+ availableSeats={serviceItem.available_seats}
861
+ isSoldOut={isSoldOut}
862
+ priceColor={colors.priceColor}
863
+ currencySign={currencySign}
864
+ removeDuplicateSeats={removeDuplicateSeats}
865
+ isPeru={isPeru}
866
+ renderIcon={renderIcon}
867
+ discountSeatPriceColor={colors.discountSeatPriceColor}
868
+ />
869
+ </div>
750
870
 
871
+ {/* BUTTON */}
872
+ {/* {showLastSeats ? (
751
873
  <div
752
- className="flex flex-col justify-between absolute inset-y-0 right-0 left-1/2 h-full"
874
+ className="flex justify-end mr-[11px] "
753
875
  style={{
754
- color: isSoldOut ? "#c0c0c0" : colors.priceColor,
755
- top: 0,
756
- bottom: 0,
757
- left: "68%",
758
- right: 0,
759
- justifyContent:
760
- getNumberOfSeats() < 2 ? "center" : "center",
761
- gap: "5px",
876
+ position: "absolute",
877
+ top: serviceDetailsLoading ? "7px" : "5px",
878
+ right: "16px",
762
879
  }}
763
880
  >
764
- <span
765
- style={{
766
- position: "absolute",
767
- top: getNumberOfSeats() > 1 ? -10 : -5,
768
- fontWeight: "initial",
769
- fontSize: "12px",
770
- left: 0,
771
- color: "#6a6a6a",
772
- }}
773
- >
774
- Desde
775
- </span>
776
- {getSeatPrice()}
881
+ {serviceItem?.available_seats < 10 &&
882
+ serviceItem?.available_seats > 0 && (
883
+ <div className="text-[12px] text-[#464647] mt-1 text-center">
884
+ ¡Últimos Asientos!
885
+ </div>
886
+ )}
777
887
  </div>
778
- </div>
779
- </div>
780
-
781
- {/* BUTTON */}
782
- <div>
783
- <button
784
- onClick={() => (!isSoldOut ? checkMidnight() : null)}
785
- disabled={serviceDetailsLoading}
786
- className={`w-full ${serviceDetailsLoading || isSoldOut ? "py-[12px]" : "py-[12px]"
787
- } text-[13.33px] font-bold text-white rounded-[10px] border-none px-[20px] flex items-center justify-center`}
788
- style={{
789
- backgroundColor:
790
- serviceDetailsLoading || isSoldOut
791
- ? "lightgray"
792
- : colors.kuposButtonColor,
793
- cursor:
794
- serviceDetailsLoading || isSoldOut
795
- ? "not-allowed"
796
- : "pointer",
797
- }}
798
- >
799
- <span className="min-w-[75px] flex justify-center items-center bold-text uppercase">
800
- {isSoldOut ? renderIcon("soldOutIcon", "14px") : null}
801
-
802
- {serviceDetailsLoading ? (
803
- <span className="loader-circle"></span>
804
- ) : !isSoldOut ? (
805
- translation?.buyButton
806
- ) : (
807
- translation?.soldOutButton
808
- )}
809
- </span>
810
- </button>
811
- </div>
812
- </div>
813
- {showLastSeats ? (
814
- <div className="flex justify-end mr-[11px]">
815
- {serviceItem?.available_seats < 10 &&
816
- serviceItem?.available_seats > 0 && (
817
- <div className="text-[12px] text-[red] mt-1 text-center">
818
- ¡ Últimos Asientos!
819
- </div>
820
- )}
821
- </div>
822
- ) : null}
823
-
824
- <div className="flex items-center mt-[15px] border-t border-[#eee] pt-[10px]">
825
- {/* 🔹 LEFT SIDE (GRID ITEMS) */}
826
- <div
827
- className="grid items-center gap-[2%] flex-1"
828
- style={{
829
- gridTemplateColumns: "30% 18% 23% 23%",
830
-
831
- // otherItems
832
- // .map((i) => i.width)
833
- // .join(" "),
834
- }}
835
- >
836
- {otherItems.map((item) => (
837
- <div key={item.key} className="flex items-center ">
838
- {item.render}
888
+ ) : null}
889
+ <KuposButton
890
+ isSoldOut={isSoldOut}
891
+ isLoading={serviceDetailsLoading}
892
+ buttonColor={colors.kuposButtonColor}
893
+ buyLabel={translation?.buyButton}
894
+ soldOutLabel={translation?.soldOutButton}
895
+ soldOutIcon={renderIcon("soldOutIcon", "14px")}
896
+ onClick={checkMidnight}
897
+ /> */}
898
+ <div className="relative">
899
+ <KuposButton
900
+ isSoldOut={isSoldOut}
901
+ isLoading={serviceDetailsLoading}
902
+ buttonColor={colors.kuposButtonColor}
903
+ buyLabel={translation?.buyButton}
904
+ soldOutLabel={translation?.soldOutButton}
905
+ soldOutIcon={renderIcon("soldOutIcon", "14px")}
906
+ onClick={checkMidnight}
907
+ />
908
+ {showLastSeats ? (
909
+ <div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
910
+ {serviceItem?.available_seats < 10 &&
911
+ serviceItem?.available_seats > 0 && (
912
+ <div
913
+ className="text-[12px] mt-1 text-center"
914
+ style={{
915
+ color: colors.seatPriceColor,
916
+ }}
917
+ >
918
+ ¡Últimos Asientos!
919
+ </div>
920
+ )}
839
921
  </div>
840
- ))}
841
- </div>
842
-
843
- {/* 🔹 RIGHT SIDE (ALWAYS END) */}
844
- <div className="flex items-center ml-[12px] shrink-0 w-[130px] justify-end">
845
- {amenitiesItem?.render}
922
+ ) : null}
846
923
  </div>
847
924
  </div>
925
+ <BottomAmenities
926
+ otherItems={otherItems}
927
+ serviceItem={serviceItem}
928
+ grayscaleClass={grayscaleClass}
929
+ isSoldOut={isSoldOut}
930
+ isItemExpanded={isItemExpanded}
931
+ colors={colors}
932
+ translation={translation}
933
+ getAnimationIcon={getAnimationIcon}
934
+ downArrowIcon={renderIcon("downArrow", "10px")}
935
+ onToggleExpand={() =>
936
+ setIsExpand && setIsExpand(isItemExpanded ? null : serviceItem.id)
937
+ }
938
+ isPeru={isPeru}
939
+ />
848
940
  </div>
849
941
  </div>
850
942
 
851
943
  {children}
852
- {/* Bottom discount banner */}
853
- {serviceItem?.offer_text && (
944
+
945
+ {/* {serviceItem?.offer_text && (
854
946
  <div
855
- 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]`}
947
+ className="text-white p-[10px_15px] text-left w-full flex items-center absolute -bottom-[36px] pt-[50px] rounded-b-[14px] text-[14px]"
856
948
  style={{
857
- backgroundColor: isSoldOut
858
- ? colors?.bottomStripColor
859
- : colors?.bottomStripColor,
949
+ background: offerGradient,
860
950
  opacity: isSoldOut ? 0.5 : 1,
861
951
  }}
862
952
  >
863
- <LottiePlayer
864
- // animationData={serviceItem.icons.promoAnim}
865
- animationData={getAnimationIcon("promoAnim")}
866
- width="18px"
867
- height="18px"
868
- />
869
- <span className="ml-[10px]">{serviceItem?.offer_text}</span>
953
+ <div className="flex justify-between items-center w-full">
954
+ <div className="flex items-center ">
955
+ <div className="flex items-center">
956
+ <LottiePlayer
957
+ animationData={getAnimationIcon("bombAnimation")}
958
+ width="18px"
959
+ height="18px"
960
+ />
961
+ <div className="flex items-center mt-[2px]">
962
+ <span className="bold-text ml-[6px]">
963
+ {(serviceItem?.offer_text || "").length > 30
964
+ ? (serviceItem?.offer_text || "").slice(0, 30) + "..."
965
+ : serviceItem?.offer_text || ""}{" "}
966
+ {isLoggedIn ? null : (
967
+ <span onClick={showLoginModal} className="cursor-pointer">
968
+ - registro
969
+ </span>
970
+ )}{" "}
971
+ &nbsp;
972
+ </span>{" "}
973
+ | Termina en&nbsp;
974
+ <span
975
+ className="bold-text text-end"
976
+ ref={(node) => CommonService.startCountdown(node, 599)}
977
+ style={{
978
+ fontVariantNumeric: "tabular-nums",
979
+ display: "inline-block",
980
+ }}
981
+ />
982
+ </div>
983
+ </div>
984
+ </div>
985
+ <div className="flex items-center">
986
+ <LottiePlayer
987
+ animationData={getAnimationIcon("dotAnimation")}
988
+ width="12px"
989
+ height="12px"
990
+ />
991
+
992
+ <span className="ml-[6px]">
993
+ <span
994
+ className="bold-text"
995
+ ref={(node) =>
996
+ CommonService.startViewerCount(node, viewersConfig)
997
+ }
998
+ style={{ fontVariantNumeric: "tabular-nums" }}
999
+ />{" "}
1000
+ <span>
1001
+ {" "}
1002
+ {viewersConfig?.label || " viendo"} |{" "}
1003
+ <span className="">
1004
+ {serviceItem?.is_dp_enabled ? null : "Quedan pocos • "}
1005
+ <span
1006
+ className="bold-text"
1007
+ ref={(node) =>
1008
+ CommonService.startComprandoCount(node, 4, 16)
1009
+ }
1010
+ style={{ fontVariantNumeric: "tabular-nums" }}
1011
+ />{" "}
1012
+ comprando
1013
+ </span>
1014
+ </span>
1015
+ </span>
1016
+ </div>
1017
+ </div>
870
1018
  </div>
871
- )}
1019
+ )} */}
1020
+
1021
+ <ServiceBadges
1022
+ showTopLabel={showTopLabel}
1023
+ isSoldOut={isSoldOut}
1024
+ colors={colors}
1025
+ renderIcon={renderIcon}
1026
+ translation={translation}
1027
+ serviceItem={serviceItem}
1028
+ />
872
1029
 
873
- <div className="absolute -top-[11px] left-0 w-full flex items-center justify-end gap-[12px] pr-[15px] z-10 ">
1030
+ {/* <div className="absolute -top-[11px] left-0 w-full flex items-center justify-end gap-[12px] pr-[15px] z-10 ">
874
1031
  {showTopLabel && (
875
1032
  <div
876
1033
  className={`flex items-center gap-[10px] py-[4px] px-[14px] rounded-[38px] text-[12.5px] z-20`}
@@ -880,7 +1037,6 @@ function PeruServiceItemDesktop({
880
1037
  >
881
1038
  <div className={isSoldOut ? "grayscale" : ""}>
882
1039
  <LottiePlayer
883
- // animationData={serviceItem.icons.priorityStageAnim}
884
1040
  animationData={getAnimationIcon("priorityStageAnim")}
885
1041
  width="14px"
886
1042
  height="14px"
@@ -905,7 +1061,6 @@ function PeruServiceItemDesktop({
905
1061
  >
906
1062
  <div className={isSoldOut ? "grayscale" : ""}>
907
1063
  <LottiePlayer
908
- // animationData={serviceItem.icons.priorityStageAnim}
909
1064
  animationData={getAnimationIcon("priorityStageAnim")}
910
1065
  width="14px"
911
1066
  height="14px"
@@ -921,30 +1076,6 @@ function PeruServiceItemDesktop({
921
1076
  </div>
922
1077
  )}
923
1078
 
924
- {/* {dropoffName && (
925
- <div
926
- className={`flex items-center gap-[10px] py-[4px] px-[14px] rounded-[38px] text-[12.5px] z-20`}
927
- style={{
928
- backgroundColor: isSoldOut ? "#ddd" : colors.ratingBottomColor,
929
- }}
930
- >
931
- <div className={isSoldOut ? "grayscale" : ""}>
932
- <LottiePlayer
933
- // animationData={serviceItem.icons.priorityStageAnim}
934
- animationData={getAnimationIcon("priorityStageAnim")}
935
- width="14px"
936
- height="14px"
937
- />
938
- </div>
939
- <div
940
- className={
941
- isSoldOut ? "text-white" : `text-[${colors.topLabelColor}]`
942
- }
943
- >
944
- {dropoffName}
945
- </div>
946
- </div>
947
- )} */}
948
1079
  {serviceItem?.is_transpordo && (
949
1080
  <div
950
1081
  className={`flex items-center gap-[10px] py-[4px] text-white px-[14px] rounded-[38px] text-[12.5px] z-20`}
@@ -952,11 +1083,8 @@ function PeruServiceItemDesktop({
952
1083
  backgroundColor: isSoldOut ? "#ddd" : colors.tooltipColor,
953
1084
  }}
954
1085
  >
955
- <LottiePlayer
956
- animationData={serviceItem.icons.connectingServiceIcon}
957
- width="14px"
958
- height="14px"
959
- />
1086
+ {renderIcon("connectingServiceIcon", "12px")}
1087
+
960
1088
  <div>{"Conexión"}</div>
961
1089
  </div>
962
1090
  )}
@@ -983,7 +1111,6 @@ function PeruServiceItemDesktop({
983
1111
  }}
984
1112
  >
985
1113
  <LottiePlayer
986
- // animationData={serviceItem.icons.directoAnim}
987
1114
  animationData={getAnimationIcon("directoAnim")}
988
1115
  width="14px"
989
1116
  height="14px"
@@ -991,7 +1118,7 @@ function PeruServiceItemDesktop({
991
1118
  <div>{"Tren Express"}</div>
992
1119
  </div>
993
1120
  )}
994
- </div>
1121
+ </div> */}
995
1122
  </div>
996
1123
  );
997
1124
  }