kupos-ui-components-lib 1.0.0

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 (177) hide show
  1. package/README copy.md +125 -0
  2. package/README.md +125 -0
  3. package/build-css.js +28 -0
  4. package/copy-assets.js +45 -0
  5. package/dist/KuposUIComponent.d.ts +84 -0
  6. package/dist/KuposUIComponent.js +49 -0
  7. package/dist/ResponsiveServiceItem.d.ts +3 -0
  8. package/dist/ResponsiveServiceItem.js +10 -0
  9. package/dist/ServiceItemDesktop.d.ts +3 -0
  10. package/dist/ServiceItemDesktop.js +6 -0
  11. package/dist/ServiceItemMobile.d.ts +3 -0
  12. package/dist/ServiceItemMobile.js +5 -0
  13. package/dist/ServiceItemMobileView.d.ts +4 -0
  14. package/dist/ServiceItemMobileView.js +33 -0
  15. package/dist/ServiceItemPb.d.ts +4 -0
  16. package/dist/ServiceItemPb.js +486 -0
  17. package/dist/ServivceItemPbMobile.d.ts +4 -0
  18. package/dist/ServivceItemPbMobile.js +248 -0
  19. package/dist/assets/LottieIcon.d.ts +7 -0
  20. package/dist/assets/LottieIcon.js +8 -0
  21. package/dist/assets/LottieIcon.tsx +26 -0
  22. package/dist/assets/LottiePlayer.d.ts +20 -0
  23. package/dist/assets/LottiePlayer.js +24 -0
  24. package/dist/assets/LottiePlayer.tsx +63 -0
  25. package/dist/assets/PopupComponent.d.ts +12 -0
  26. package/dist/assets/PopupComponent.js +18 -0
  27. package/dist/assets/PopupComponent.tsx +65 -0
  28. package/dist/assets/images/amenities/Icon_More-Amenities.svg +1 -0
  29. package/dist/assets/images/amenities/Restrooms-white.svg +1 -0
  30. package/dist/assets/images/amenities/Restrooms.svg +1 -0
  31. package/dist/assets/images/amenities/air_condtion-white.svg +1 -0
  32. package/dist/assets/images/amenities/air_condtion.svg +1 -0
  33. package/dist/assets/images/amenities/amenity-cortina.png +0 -0
  34. package/dist/assets/images/amenities/baggage-white.svg +1 -0
  35. package/dist/assets/images/amenities/baggage.svg +1 -0
  36. package/dist/assets/images/amenities/charging_plug-white.svg +1 -0
  37. package/dist/assets/images/amenities/charging_plug.svg +17 -0
  38. package/dist/assets/images/amenities/coffee.svg +1 -0
  39. package/dist/assets/images/amenities/cortina_divisoria.svg +19 -0
  40. package/dist/assets/images/amenities/divider-curtain.svg +34 -0
  41. package/dist/assets/images/amenities/food_new_icon-white.svg +1 -0
  42. package/dist/assets/images/amenities/food_new_icon.svg +1 -0
  43. package/dist/assets/images/amenities/frazda_amenity.svg +11 -0
  44. package/dist/assets/images/amenities/handicap-white.svg +1 -0
  45. package/dist/assets/images/amenities/handicap.svg +1 -0
  46. package/dist/assets/images/amenities/icon-app-cortina.svg +19 -0
  47. package/dist/assets/images/amenities/mobile_ticket-white.svg +1 -0
  48. package/dist/assets/images/amenities/mobile_ticket.svg +29 -0
  49. package/dist/assets/images/amenities/movie-white.svg +1 -0
  50. package/dist/assets/images/amenities/movie.svg +1 -0
  51. package/dist/assets/images/amenities/music-white.svg +1 -0
  52. package/dist/assets/images/amenities/music.svg +1 -0
  53. package/dist/assets/images/amenities/snacks_new-white.svg +1 -0
  54. package/dist/assets/images/amenities/snacks_new.svg +27 -0
  55. package/dist/assets/images/amenities/whatsapp-charges-info-icon.svg +5 -0
  56. package/dist/assets/images/amenities/wifi-white.svg +1 -0
  57. package/dist/assets/images/amenities/wifi.svg +1 -0
  58. package/dist/assets/images/anims/service_list/flexible.json +391 -0
  59. package/dist/assets/images/anims/service_list/location.json +589 -0
  60. package/dist/assets/images/index.d.ts +32 -0
  61. package/dist/assets/images/index.js +35 -0
  62. package/dist/assets/images/index.ts +35 -0
  63. package/dist/components/InternationalServicePopupBody.d.ts +3 -0
  64. package/dist/components/InternationalServicePopupBody.js +21 -0
  65. package/dist/components/PaymentSideBar/PaymentSideBarDesktop.d.ts +4 -0
  66. package/dist/components/PaymentSideBar/PaymentSideBarDesktop.js +107 -0
  67. package/dist/components/PaymentSideBar/PaymentSideBarMobile.d.ts +4 -0
  68. package/dist/components/PaymentSideBar/PaymentSideBarMobile.js +115 -0
  69. package/dist/components/PaymentSideBar/ResponsivePaymentSideBar.d.ts +3 -0
  70. package/dist/components/PaymentSideBar/ResponsivePaymentSideBar.js +10 -0
  71. package/dist/components/PaymentSideBar/index.d.ts +5 -0
  72. package/dist/components/PaymentSideBar/index.js +4 -0
  73. package/dist/components/PaymentSideBar/types.d.ts +38 -0
  74. package/dist/components/PaymentSideBar/types.js +1 -0
  75. package/dist/components/ServiceItem/ResponsiveServiceItem.d.ts +3 -0
  76. package/dist/components/ServiceItem/ResponsiveServiceItem.js +10 -0
  77. package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +4 -0
  78. package/dist/components/ServiceItem/ServiceItemDesktop.js +486 -0
  79. package/dist/components/ServiceItem/ServiceItemMobile.d.ts +4 -0
  80. package/dist/components/ServiceItem/ServiceItemMobile.js +248 -0
  81. package/dist/components/ServiceItem/index.d.ts +5 -0
  82. package/dist/components/ServiceItem/index.js +4 -0
  83. package/dist/components/ServiceItem/mobileTypes.d.ts +141 -0
  84. package/dist/components/ServiceItem/mobileTypes.js +1 -0
  85. package/dist/components/ServiceItem/types.d.ts +164 -0
  86. package/dist/components/ServiceItem/types.js +1 -0
  87. package/dist/components/ServiceList/ResponsiveServiceList.d.ts +3 -0
  88. package/dist/components/ServiceList/ResponsiveServiceList.js +10 -0
  89. package/dist/components/ServiceList/ServiceListDesktop.d.ts +4 -0
  90. package/dist/components/ServiceList/ServiceListDesktop.js +164 -0
  91. package/dist/components/ServiceList/ServiceListMobile.d.ts +4 -0
  92. package/dist/components/ServiceList/ServiceListMobile.js +195 -0
  93. package/dist/components/ServiceList/index.d.ts +5 -0
  94. package/dist/components/ServiceList/index.js +4 -0
  95. package/dist/components/ServiceList/types.d.ts +35 -0
  96. package/dist/components/ServiceList/types.js +1 -0
  97. package/dist/example.d.ts +5 -0
  98. package/dist/example.js +240 -0
  99. package/dist/index.d.ts +13 -0
  100. package/dist/index.js +15 -0
  101. package/dist/mobileTypes.d.ts +141 -0
  102. package/dist/mobileTypes.js +1 -0
  103. package/dist/styles.css +803 -0
  104. package/dist/types.d.ts +151 -0
  105. package/dist/types.js +1 -0
  106. package/dist/utils/DateService.d.ts +16 -0
  107. package/dist/utils/DateService.js +160 -0
  108. package/dist/utils/ModalEventManager.d.ts +40 -0
  109. package/dist/utils/ModalEventManager.js +329 -0
  110. package/kupos-service-item-package-1.0.0.tgz +0 -0
  111. package/package.json +43 -0
  112. package/postcss.config.js +6 -0
  113. package/src/KuposUIComponent.tsx +152 -0
  114. package/src/ServiceItemPb.tsx +1048 -0
  115. package/src/assets/LottieIcon.tsx +26 -0
  116. package/src/assets/LottiePlayer.tsx +63 -0
  117. package/src/assets/PopupComponent.tsx +65 -0
  118. package/src/assets/images/amenities/Icon_More-Amenities.svg +1 -0
  119. package/src/assets/images/amenities/Restrooms-white.svg +1 -0
  120. package/src/assets/images/amenities/Restrooms.svg +1 -0
  121. package/src/assets/images/amenities/air_condtion-white.svg +1 -0
  122. package/src/assets/images/amenities/air_condtion.svg +1 -0
  123. package/src/assets/images/amenities/amenity-cortina.png +0 -0
  124. package/src/assets/images/amenities/baggage-white.svg +1 -0
  125. package/src/assets/images/amenities/baggage.svg +1 -0
  126. package/src/assets/images/amenities/charging_plug-white.svg +1 -0
  127. package/src/assets/images/amenities/charging_plug.svg +17 -0
  128. package/src/assets/images/amenities/coffee.svg +1 -0
  129. package/src/assets/images/amenities/cortina_divisoria.svg +19 -0
  130. package/src/assets/images/amenities/divider-curtain.svg +34 -0
  131. package/src/assets/images/amenities/food_new_icon-white.svg +1 -0
  132. package/src/assets/images/amenities/food_new_icon.svg +1 -0
  133. package/src/assets/images/amenities/frazda_amenity.svg +11 -0
  134. package/src/assets/images/amenities/handicap-white.svg +1 -0
  135. package/src/assets/images/amenities/handicap.svg +1 -0
  136. package/src/assets/images/amenities/icon-app-cortina.svg +19 -0
  137. package/src/assets/images/amenities/mobile_ticket-white.svg +1 -0
  138. package/src/assets/images/amenities/mobile_ticket.svg +29 -0
  139. package/src/assets/images/amenities/movie-white.svg +1 -0
  140. package/src/assets/images/amenities/movie.svg +1 -0
  141. package/src/assets/images/amenities/music-white.svg +1 -0
  142. package/src/assets/images/amenities/music.svg +1 -0
  143. package/src/assets/images/amenities/snacks_new-white.svg +1 -0
  144. package/src/assets/images/amenities/snacks_new.svg +27 -0
  145. package/src/assets/images/amenities/whatsapp-charges-info-icon.svg +5 -0
  146. package/src/assets/images/amenities/wifi-white.svg +1 -0
  147. package/src/assets/images/amenities/wifi.svg +1 -0
  148. package/src/assets/images/anims/service_list/flexible.json +391 -0
  149. package/src/assets/images/anims/service_list/location.json +589 -0
  150. package/src/assets/images/index.ts +35 -0
  151. package/src/components/InternationalServicePopupBody.tsx +40 -0
  152. package/src/components/PaymentSideBar/PaymentSideBarDesktop.tsx +183 -0
  153. package/src/components/PaymentSideBar/PaymentSideBarMobile.tsx +197 -0
  154. package/src/components/PaymentSideBar/ResponsivePaymentSideBar.tsx +14 -0
  155. package/src/components/PaymentSideBar/index.ts +5 -0
  156. package/src/components/PaymentSideBar/types.ts +44 -0
  157. package/src/components/ServiceItem/ResponsiveServiceItem.tsx +14 -0
  158. package/src/components/ServiceItem/ServiceItemDesktop.tsx +1048 -0
  159. package/src/components/ServiceItem/ServiceItemMobile.tsx +544 -0
  160. package/src/components/ServiceItem/index.ts +5 -0
  161. package/src/components/ServiceItem/mobileTypes.ts +159 -0
  162. package/src/components/ServiceItem/types.ts +163 -0
  163. package/src/components/ServiceList/ResponsiveServiceList.tsx +14 -0
  164. package/src/components/ServiceList/ServiceListDesktop.tsx +353 -0
  165. package/src/components/ServiceList/ServiceListMobile.tsx +400 -0
  166. package/src/components/ServiceList/index.ts +5 -0
  167. package/src/components/ServiceList/types.ts +43 -0
  168. package/src/dummyData.json +73 -0
  169. package/src/example.tsx +348 -0
  170. package/src/index.ts +37 -0
  171. package/src/lottie-player.d.ts +0 -0
  172. package/src/styles.css +77 -0
  173. package/src/types.ts +150 -0
  174. package/src/utils/DateService.ts +197 -0
  175. package/src/utils/ModalEventManager.ts +405 -0
  176. package/tailwind.config.js +39 -0
  177. package/tsconfig.json +18 -0
@@ -0,0 +1,1048 @@
1
+ import React from "react";
2
+ import { ServiceItemProps } from "./types";
3
+ import LottiePlayer from "./assets/LottiePlayer";
4
+ import DateService from "./utils/DateService";
5
+ import ModalEventManager from "./utils/ModalEventManager";
6
+ import InternationalServicePopupBody from "./components/InternationalServicePopupBody";
7
+
8
+ const SEAT_EXCEPTIONS = ["Asiento mascota"];
9
+
10
+ function ServiceItemPB({
11
+ serviceItem,
12
+ onBookButtonPress,
13
+ colors,
14
+ metaData,
15
+ children,
16
+ busStage,
17
+ serviceDetailsLoading,
18
+ cityOrigin,
19
+ cityDestination,
20
+ translation,
21
+ orignLabel,
22
+ destinationLabel,
23
+ t = (key: string) => key,
24
+ }: ServiceItemProps): React.ReactElement {
25
+ const SvgAmenities = ({
26
+ moreAnemities,
27
+ name,
28
+ color,
29
+ }: {
30
+ moreAnemities: boolean;
31
+ name: string;
32
+ color?: string;
33
+ }) => {
34
+ const amenityKey = name.toLowerCase().replace(/\s/g, "_");
35
+
36
+ const getIconPath = () => {
37
+ const amenityFileName = `${amenityKey}.png`;
38
+ const imagePath = getAmenitiesImage(amenityFileName);
39
+
40
+ if (!imagePath) {
41
+ return `/public/images/amenities/${amenityKey}.svg`;
42
+ }
43
+
44
+ return imagePath;
45
+ };
46
+
47
+ const iconPath = getIconPath();
48
+
49
+ return (
50
+ <img
51
+ src={iconPath}
52
+ alt={name}
53
+ style={{
54
+ filter: color === "white" ? "brightness(0) invert(1)" : "",
55
+ }}
56
+ className={`object-contain ${
57
+ moreAnemities ? "w-[20px] h-[20px]" : "w-[16px] h-[16px]"
58
+ }`}
59
+ />
60
+ );
61
+ };
62
+
63
+ const getAmenitiesImage = (name: string): string => {
64
+ switch (name) {
65
+ case "air_condtion.png": {
66
+ return serviceItem?.icons?.airConditionIcon;
67
+ }
68
+ case "baggage.png": {
69
+ return serviceItem?.icons?.baggageIcon;
70
+ }
71
+ case "charging_plug.png": {
72
+ return serviceItem?.icons?.chargingIcon;
73
+ }
74
+ case "coffee.png": {
75
+ return serviceItem?.icons?.coffeeIcon;
76
+ }
77
+ case "food_new_icon.png": {
78
+ return serviceItem?.icons?.foodIcon;
79
+ }
80
+ case "gaming.png": {
81
+ return serviceItem?.icons?.gamingIcon;
82
+ }
83
+ case "handicap.png": {
84
+ return serviceItem?.icons?.handicapIcon;
85
+ }
86
+ case "mobile_ticket.png": {
87
+ return serviceItem?.icons?.mobileTicketIcon;
88
+ }
89
+ case "movie.png": {
90
+ return serviceItem?.icons?.movieIcon;
91
+ }
92
+ case "restrooms.png": {
93
+ return serviceItem?.icons?.restroomsIcon;
94
+ }
95
+ case "snacks_new.png": {
96
+ return serviceItem?.icons?.snackIcon;
97
+ }
98
+ case "wifi.png": {
99
+ return serviceItem?.icons?.wifiIcon;
100
+ }
101
+ case "cortina_divisoria.png": {
102
+ return serviceItem?.icons?.cortinaIcon;
103
+ }
104
+ case "frazada.png": {
105
+ return serviceItem?.icons?.frazaIcon;
106
+ }
107
+ default: {
108
+ return "";
109
+ }
110
+ }
111
+ };
112
+
113
+ const getAmenityName = (rawAmenity: string): string => {
114
+ switch (rawAmenity) {
115
+ case "mobile ticket":
116
+ return "Ticket móvil";
117
+ case "charging plug":
118
+ return "Cargador";
119
+ case "wifi":
120
+ return "WiFi";
121
+ case "movie":
122
+ return "Entretenimiento";
123
+ case "baggage":
124
+ return "Equipaje";
125
+ case "Restrooms":
126
+ return "Baños";
127
+ case "air condtion":
128
+ return "Aire acondicionado";
129
+ case "snacks new":
130
+ return "Snacks";
131
+ case "coffee":
132
+ return "Café";
133
+ case "cortina divisoria":
134
+ return "Cortina Divisoria";
135
+ case "frazada":
136
+ return "";
137
+ default:
138
+ return rawAmenity;
139
+ }
140
+ };
141
+
142
+ const currency = (amount: number) => {
143
+ const formattedAmount = amount
144
+ .toString()
145
+ .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
146
+ return "$" + formattedAmount;
147
+ };
148
+
149
+ const labelId =
150
+ typeof serviceItem.boarding_stages === "string"
151
+ ? serviceItem.boarding_stages.split("|")[0]
152
+ : "";
153
+
154
+ const showTopLabel =
155
+ busStage &&
156
+ busStage[labelId] &&
157
+ busStage[labelId].split("|")[1] === "true" &&
158
+ busStage[labelId].split("|")[0];
159
+
160
+ const renderStages = (stageData: any) => {
161
+ if (typeof stageData === "string") {
162
+ return (
163
+ <div className="flex flex-col space-y-1 text-justify gap-[4px]">
164
+ {stageData.split(",").map((stageInfo: string, index: number) => {
165
+ const parts = stageInfo.split("|");
166
+ const id = parts[0];
167
+ const time = parts[1];
168
+ const label = parts[3] || ""; // Skip empty part [2]
169
+ return (
170
+ <div key={index}>
171
+ {label} | {time}
172
+ </div>
173
+ );
174
+ })}
175
+ </div>
176
+ );
177
+ }
178
+ return null;
179
+ };
180
+ let isSoldOut = serviceItem.available_seats <= 0;
181
+
182
+ const renderIcon = (iconKey: string, size: string = "14px") => {
183
+ const iconValue = serviceItem.icons?.[iconKey];
184
+ if (iconValue) {
185
+ if (typeof iconValue === "string") {
186
+ return (
187
+ <img
188
+ src={iconValue}
189
+ alt={iconKey}
190
+ className={`${`w-[${size}] h-[${size}]`} mr-[5px]`}
191
+ />
192
+ );
193
+ }
194
+ }
195
+ return null;
196
+ };
197
+
198
+ const getSortedSeatTypes = () => {
199
+ if (!serviceItem?.seat_types?.length) {
200
+ return [{ label: "Salon cama", price: 0 }];
201
+ }
202
+
203
+ let seatTypesWithPrices = serviceItem.seat_types
204
+ .filter((item) => getFilteredSeats(item))
205
+ .map((val) => ({
206
+ label: val?.label,
207
+ price: val?.fare,
208
+ }));
209
+
210
+ seatTypesWithPrices.sort((a, b) => a.price - b.price);
211
+ const premiumIndex = seatTypesWithPrices.findIndex(
212
+ (item) => item.label === "Premium"
213
+ );
214
+
215
+ if (premiumIndex >= 3) {
216
+ seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
217
+ }
218
+
219
+ seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
220
+
221
+ return seatTypesWithPrices;
222
+ };
223
+
224
+ const getNumberOfSeats = () => {
225
+ return serviceItem.seat_types.filter(
226
+ (val) => !SEAT_EXCEPTIONS.includes(val.label)
227
+ ).length;
228
+ };
229
+
230
+ const getSeatNames = () => {
231
+ const sortedSeatTypes = getSortedSeatTypes();
232
+ return sortedSeatTypes.map((val, key: number) =>
233
+ SEAT_EXCEPTIONS.includes(val.label) ? null : (
234
+ <span
235
+ key={key}
236
+ className={`flex items-center justify-between text-[13.33px] ${
237
+ isSoldOut ? "text-[#c0c0c0]" : ""
238
+ }`}
239
+ >
240
+ {typeof val.label === "string" || typeof val.label === "number"
241
+ ? val.label
242
+ : null}
243
+ </span>
244
+ )
245
+ );
246
+ };
247
+
248
+ const getSeatPrice = () => {
249
+ const sortedSeatTypes = getSortedSeatTypes();
250
+ return sortedSeatTypes.map((val, key: number) =>
251
+ SEAT_EXCEPTIONS.includes(val.label) ? null : (
252
+ <span
253
+ key={key}
254
+ className="flex items-center justify-between text-[13.33px]"
255
+ >
256
+ {typeof val.price === "string"
257
+ ? currency(val.price)
258
+ : typeof val.price === "number"
259
+ ? currency(val.price)
260
+ : null}
261
+ </span>
262
+ )
263
+ );
264
+ };
265
+
266
+ const getFilteredSeats = (item) => {
267
+ return item;
268
+ };
269
+
270
+ const checkMidnight = () => {
271
+ if (
272
+ cityOrigin?.label &&
273
+ cityDestination?.label &&
274
+ ((cityOrigin.label.toLowerCase().includes("argentina") &&
275
+ !cityDestination.label.toLowerCase().includes("argentina")) ||
276
+ (!cityOrigin.label.toLowerCase().includes("argentina") &&
277
+ cityDestination.label.toLowerCase().includes("argentina")))
278
+ ) {
279
+ ModalEventManager.showPopup({
280
+ modalIcon: serviceItem?.icons?.warningIcon,
281
+ title: translation?.title,
282
+ showPrimaryButton: false,
283
+ primaryButtonText: translation?.continueButton,
284
+ onSecondaryButtonClick: () => {
285
+ onBookButtonPress && onBookButtonPress();
286
+ },
287
+ secondaryButtonText: translation?.okContinueButton,
288
+ primaryButtonBgColor: colors?.kuposButtonColor,
289
+ primaryButtonTextColor: colors?.primaryButtonTextColor,
290
+ secondaryButtonBgColor: colors?.secondaryBgColor,
291
+ secondaryButtonTextColor: colors?.secondaryTextColor,
292
+ children: <InternationalServicePopupBody />,
293
+ });
294
+ return;
295
+ }
296
+
297
+ if (serviceItem.dep_validation_text) {
298
+ ModalEventManager.showPopup({
299
+ body: serviceItem.dep_validation_text,
300
+ primaryButtonText: translation?.continueButton,
301
+ secondaryButtonText: translation?.chooseAnotherTripButton,
302
+ onPrimaryButtonClick: () => {
303
+ onBookButtonPress && onBookButtonPress();
304
+ },
305
+ primaryButtonBgColor: colors?.kuposButtonColor,
306
+ primaryButtonTextColor: colors?.primaryButtonTextColor,
307
+ secondaryButtonBgColor: colors?.secondaryBgColor,
308
+ secondaryButtonTextColor: colors?.secondaryTextColor,
309
+ });
310
+ return;
311
+ }
312
+
313
+ if (
314
+ serviceItem.operator_service_name === "Classe Reale " &&
315
+ cityOrigin.label.toLowerCase() === "santiago,chile" &&
316
+ cityDestination.label.toLowerCase() === "portillo,chile"
317
+ ) {
318
+ ModalEventManager.showPopup({
319
+ modalIcon: serviceItem?.icons?.warningIcon,
320
+ title: translation?.title,
321
+ primaryButtonText: translation?.okContinueButton,
322
+ secondaryButtonText: translation?.chooseAnotherTripButton,
323
+ onPrimaryButtonClick: () => {
324
+ onBookButtonPress && onBookButtonPress();
325
+ },
326
+ showSecondaryButton: false,
327
+ primaryButtonBgColor: colors?.kuposButtonColor,
328
+ primaryButtonTextColor: colors?.primaryButtonTextColor,
329
+ children: (
330
+ <>
331
+ <p className="mt-[5px] text-justify">
332
+ {translation.classeNormalText}
333
+ <span className="bold-text">
334
+ &nbsp;{translation.classeBoldText}
335
+ </span>
336
+ {translation.classeNormalText1}
337
+ </p>
338
+ <p className="mt-[10px] text-justify">
339
+ {translation.classeNormalText2}&nbsp;
340
+ <span className="bold-text">
341
+ &nbsp;{translation.classeBoldText1}
342
+ </span>{" "}
343
+ {translation.classeNormalText3}
344
+ </p>
345
+ </>
346
+ ),
347
+ });
348
+ return;
349
+ } else {
350
+ onBookButtonPressHandler();
351
+ }
352
+ };
353
+
354
+ const onBookButtonPressHandler = () => {
355
+ onBookButtonPress();
356
+ };
357
+
358
+ return (
359
+ <div
360
+ className={`relative ${
361
+ serviceItem.offer_text || serviceItem?.is_direct_trip
362
+ ? "mb-[60px]"
363
+ : "mb-[20px]"
364
+ } ${
365
+ serviceItem?.is_direct_trip ||
366
+ serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
367
+ showTopLabel
368
+ ? "mt-[30px]"
369
+ : "mt-[20px]"
370
+ } `}
371
+ // ${
372
+ // serviceItem?.is_direct_trip ||
373
+ // serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
374
+ // !showTopLabel
375
+ // ? "mb-[60px]"
376
+ // : ""
377
+ // }
378
+ >
379
+ <div
380
+ className={
381
+ "bg-white rounded-[20px] shadow-service mb-[10px] mx-auto relative"
382
+ }
383
+ >
384
+ <div className="p-[15px]">
385
+ {/* Header with operator info and favorite */}
386
+ <div className="flex justify-between items-center mb-[15px]">
387
+ <div className="w-[120px] overflow-y-hidden">
388
+ <img
389
+ src={serviceItem.operator_details[0]}
390
+ alt="service logo"
391
+ className={`w-[120px] h-auto object-contain ${
392
+ isSoldOut ? "grayscale" : ""
393
+ }`}
394
+ />
395
+ </div>
396
+ </div>
397
+
398
+ {/* <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]"> */}
399
+ <div className="grid text-[#464647] w-full [grid-template-columns:minmax(0,1.4fr)_minmax(0,0.2fr)_minmax(0,1fr)_auto] gap-x-[7rem] min-[640px]:gap-x-[6rem] min-[768px]:gap-x-[4rem] min-[1024px]:gap-x-[4rem] min-[1280px]:gap-x-[4rem] min-[1380px]:gap-x-[5rem]">
400
+ {/* DATE AND TIME */}
401
+ <div className="min-h-[2.5rem] flex flex-col justify-between gap-[10px]">
402
+ <div
403
+ className={`flex items-center text-[13.33px] justify-between ${
404
+ isSoldOut ? "text-[#c0c0c0]" : ""
405
+ }`}
406
+ >
407
+ <div className="flex items-center bold-text capitalize group ">
408
+ {orignLabel ? (
409
+ <div className="w-[60px]">{orignLabel}</div>
410
+ ) : (
411
+ <div className="w-[18px] h-auto mr-[8px]">
412
+ <img
413
+ src={serviceItem.icons?.origin}
414
+ alt="origin"
415
+ className={`w-[18px] h-auto mr-[8px] ${
416
+ isSoldOut ? "grayscale" : ""
417
+ }`}
418
+ />
419
+ </div>
420
+ )}
421
+ <span className="cursor-pointer bold-text">
422
+ {DateService.getServiceItemDate(serviceItem.travel_date)}
423
+ </span>
424
+
425
+ {/* Boarding stage tooltip */}
426
+ {serviceItem.boarding_stages && (
427
+ <div
428
+ className="hidden group-hover:block absolute top-[29%] left-[35%] ml-2 text-white px-3 py-2 rounded-[10px] whitespace-normal z-10 shadow-service "
429
+ style={{ backgroundColor: colors?.tooltipColor }}
430
+ >
431
+ <div
432
+ className="tooltip-arrow absolute top-2 -left-[7px] w-0 h-0 border-t-8 border-b-8 border-r-8 border-t-transparent border-b-transparent"
433
+ style={{ borderRightColor: colors?.tooltipColor }}
434
+ ></div>
435
+ <div className="text-center text-[14px]">
436
+ {renderStages(serviceItem.boarding_stages)}
437
+ </div>
438
+ </div>
439
+ )}
440
+ </div>
441
+ <div className="mx-[8px]">•</div>
442
+ <div className="font-[900] relative">
443
+ {DateService.formatTime(serviceItem.dep_time)}
444
+ </div>
445
+ </div>
446
+ <div
447
+ className={`flex items-center text-[13.33px] justify-between ${
448
+ isSoldOut ? "text-[#c0c0c0]" : ""
449
+ }`}
450
+ >
451
+ <div className="flex items-center bold-text capitalize group ">
452
+ {destinationLabel ? (
453
+ <div className="w-[60px]">{destinationLabel}</div>
454
+ ) : (
455
+ <div className="w-[18px] h-auto mr-[8px]">
456
+ <img
457
+ src={serviceItem.icons?.destination}
458
+ className={`w-[18px] h-auto mr-[8px] ${
459
+ isSoldOut ? "grayscale" : ""
460
+ }`}
461
+ />
462
+ </div>
463
+ )}
464
+ <span className="cursor-pointer bold-text">
465
+ {DateService.getServiceItemDate(serviceItem.arrival_date)}
466
+ </span>
467
+
468
+ {/* Dropping stage tooltip */}
469
+ {serviceItem.dropoff_stages && (
470
+ <div
471
+ className={`hidden group-hover:block absolute top-[46%] left-[35%] ml-2 text-white px-3 py-2 rounded-[10px] whitespace-normal z-10 shadow-service`}
472
+ style={{ backgroundColor: colors?.tooltipColor }}
473
+ >
474
+ {/* Tooltip arrow */}
475
+ <div
476
+ className="tooltip-arrow absolute top-2 -left-[7px] w-0 h-0 border-t-8 border-b-8 border-r-8 border-t-transparent border-b-transparent"
477
+ style={{ borderRightColor: colors?.tooltipColor }}
478
+ ></div>
479
+ <div className="text-center text-[14px]">
480
+ {renderStages(serviceItem.dropoff_stages)}
481
+ </div>
482
+ </div>
483
+ )}
484
+ </div>
485
+ <div className="mx-[8px]">•</div>
486
+ <div className="font-[900]">
487
+ {DateService.formatTime(serviceItem.arr_time)}
488
+ </div>
489
+ </div>
490
+ </div>
491
+ <div
492
+ style={{
493
+ width: "1px",
494
+ height: "2.5rem",
495
+ backgroundColor: "#ccc",
496
+ margin: "auto",
497
+ }}
498
+ ></div>
499
+ {/* SEATS */}
500
+ <div className="content-center">
501
+ <div
502
+ className={`flex gap-[10px] text-[13.33px] justify-between min-h-[2.5rem] ${
503
+ getNumberOfSeats() < 3 ? "" : ""
504
+ }`}
505
+ style={getNumberOfSeats() < 2 ? { alignItems: "center" } : {}}
506
+ >
507
+ <div
508
+ className="flex flex-col justify-between"
509
+ // className={`flex flex-col ${
510
+ // getNumberOfSeats() < 3 ? "justify-between" : ""
511
+ // }`}
512
+ >
513
+ {getSeatNames()}
514
+ </div>
515
+ <div
516
+ className="flex flex-col justify-between"
517
+ // className={`flex flex-col ${
518
+ // getNumberOfSeats() < 3 ? "justify-between" : ""
519
+ // }`}
520
+ style={{ color: isSoldOut ? "#c0c0c0" : colors.priceColor }}
521
+ >
522
+ {getSeatPrice()}
523
+ </div>
524
+ </div>
525
+ </div>
526
+
527
+ {/* BUTTON */}
528
+ <div>
529
+ <button
530
+ onClick={() => (!isSoldOut ? checkMidnight() : null)}
531
+ disabled={serviceDetailsLoading}
532
+ className={`w-full ${
533
+ serviceDetailsLoading || isSoldOut ? "py-[6px]" : "py-[12px]"
534
+ } text-[13.33px] font-bold text-white rounded-[10px] border-none px-[20px] flex items-center justify-center`}
535
+ style={{
536
+ backgroundColor:
537
+ serviceDetailsLoading || isSoldOut
538
+ ? "lightgray"
539
+ : colors.kuposButtonColor,
540
+ cursor:
541
+ serviceDetailsLoading || isSoldOut
542
+ ? "not-allowed"
543
+ : "pointer",
544
+ }}
545
+ >
546
+ <span className="min-w-[75px] flex justify-center items-center bold-text">
547
+ {isSoldOut ? renderIcon("soldOutIcon", "14px") : null}
548
+
549
+ {serviceDetailsLoading ? (
550
+ <span className="loader-circle"></span>
551
+ ) : !isSoldOut ? (
552
+ translation?.buyButton
553
+ ) : (
554
+ translation?.soldOutButton
555
+ )}
556
+ </span>
557
+ </button>
558
+ </div>
559
+ </div>
560
+ <div className="flex justify-end mr-[11px]">
561
+ {serviceItem?.available_seats < 10 &&
562
+ serviceItem?.available_seats > 0 && (
563
+ <div className="text-[12px] text-[red] mt-1 text-center">
564
+ ¡ Últimos Asientos!
565
+ </div>
566
+ )}
567
+ </div>
568
+
569
+ <div
570
+ // className={`${
571
+ // serviceItem.is_change_ticket &&
572
+ // serviceItem.pet_seat_info &&
573
+ // serviceItem?.is_tracking_enabled &&
574
+ // Object.keys(serviceItem.pet_seat_info || {}).length > 0
575
+ // ? "grid grid-cols-[1.4fr_4.8fr] gap-[3.4rem] mt-[15px] text-[13px] items-center border-t border-[#eee] mt-[15px] pt-[12px]"
576
+ // : "grid grid-cols-[3.17fr_4.8fr] gap-[3rem] mt-[15px] text-[13px] items-center border-t border-[#eee] mt-[15px] pt-[12px]"
577
+ // }`}
578
+ className={`${"flex justify-between items-center mt-[15px] items-center border-t border-[#eee] mt-[15px] pt-[12px]"}`}
579
+ >
580
+ {/* Rating */}
581
+ <div>
582
+ <div className="flex items-center ">
583
+ <div
584
+ className="flex items-center cursor-pointer "
585
+ style={{ color: isSoldOut ? "#c0c0c0" : "" }}
586
+ >
587
+ <div className="flex items-center">
588
+ <div className="w-[18px] h-auto mr-[4px] relative">
589
+ <img
590
+ src={serviceItem.icons.rating}
591
+ alt="origin"
592
+ className={`w-[16px] h-[16px] mr-[4px] object-contain mb-[4px] ${
593
+ isSoldOut ? "grayscale" : ""
594
+ }`}
595
+ onMouseEnter={(e) => {
596
+ const tooltip = e.currentTarget
597
+ .nextElementSibling as HTMLElement;
598
+ if (tooltip) tooltip.style.display = "block";
599
+ }}
600
+ onMouseLeave={(e) => {
601
+ const tooltip = e.currentTarget
602
+ .nextElementSibling as HTMLElement;
603
+ if (tooltip) tooltip.style.display = "none";
604
+ }}
605
+ />
606
+ <div
607
+ className="hidden group-hover:block absolute left-[80px] -bottom-[160px] z-20 mt-2 w-[280px] rounded-lg shadow-service-2 bg-white overflow-hidden rounded-[14px] border-[2px]"
608
+ style={{
609
+ borderColor: colors.ratingBorderColor,
610
+ color: isSoldOut ? "#c0c0c0" : "",
611
+ }}
612
+ >
613
+ <div className="pt-[20px] text-center">
614
+ <div className="text-[12px] bold-text text-[#464647]">
615
+ PUNTUACIÓN
616
+ </div>
617
+ <div className="text-[12px] font-light text-[#464647]">
618
+ {serviceItem.operator_service_name}
619
+ </div>
620
+ </div>
621
+
622
+ {/* Rating categories */}
623
+ <div className="px-3 py-2 flex flex-col gap-[10px]">
624
+ {[
625
+ { key: "1", label: "Calidad del bus" },
626
+ { key: "2", label: "Puntualidad" },
627
+ {
628
+ key: "3",
629
+ label: "Calidad del servicio",
630
+ },
631
+ {
632
+ key: "4",
633
+ label: "Recomendación del servicio",
634
+ },
635
+ ].map((rating, index, array) => {
636
+ const isLast = index === array.length - 1;
637
+ // Use operator_details[6] for rating values
638
+ const ratingData =
639
+ serviceItem.operator_details[6] || {};
640
+ const value = ratingData?.[rating.key] || 0;
641
+ return (
642
+ <div
643
+ key={rating.key}
644
+ className={`flex items-center ${
645
+ isLast ? "mb-2" : ""
646
+ }`}
647
+ >
648
+ <div className="relative w-[3rem] h-[3rem]">
649
+ <div className="absolute inset-0 w-full h-full rounded-full">
650
+ <div className="w-full h-full rounded-full border-[2px] border-[lightgray]"></div>
651
+ </div>
652
+
653
+ {/* Colored progress arc based on value */}
654
+ <div className="absolute inset-0 w-full h-full">
655
+ <svg
656
+ className="w-full h-full"
657
+ viewBox="0 0 100 100"
658
+ >
659
+ <circle
660
+ cx="50"
661
+ cy="50"
662
+ r="48"
663
+ fill="none"
664
+ strokeWidth="4"
665
+ stroke={
666
+ value >= 4
667
+ ? "#00A650"
668
+ : colors.ratingBorderColor
669
+ }
670
+ strokeDasharray={
671
+ value > 0
672
+ ? `${value * 75.4} 301.6`
673
+ : "150.8 301.6"
674
+ }
675
+ strokeDashoffset="75.4"
676
+ transform="rotate(-90, 50, 50)"
677
+ />
678
+ </svg>
679
+ </div>
680
+
681
+ {/* Center with value */}
682
+ <div className="absolute inset-0 flex items-center justify-center top-[40%] left-[36%]">
683
+ <span className="text-[#464647] font-medium text-[12px] ">
684
+ {value.toFixed(1)}
685
+ </span>
686
+ </div>
687
+ </div>
688
+
689
+ <span className="text-[#464647] text-[13.33px] ml-[10px]">
690
+ {rating.label}
691
+ </span>
692
+ </div>
693
+ );
694
+ })}
695
+ </div>
696
+
697
+ {/* Footer */}
698
+ <div
699
+ className="px-1 py-2 text-center text-[12px] text-[#ff8f45]"
700
+ style={{ backgroundColor: colors.ratingBottomColor }}
701
+ >
702
+ Esta puntuación se obtuvo de las opiniones de{" "}
703
+ {serviceItem.operator_details[5] || 0} usuarios.
704
+ </div>
705
+ </div>
706
+ </div>
707
+ <span className="text-[#464647] bold-text text-[13.33px]">
708
+ {typeof serviceItem.operator_details[1] === "number"
709
+ ? serviceItem.operator_details[1].toFixed(1)
710
+ : serviceItem.operator_details[1]}
711
+ </span>
712
+ </div>
713
+ <span className="ml-[10px] text-[#464647] text-[13.33px]">
714
+ {serviceItem.operator_details[2]}
715
+ </span>
716
+ </div>
717
+
718
+ {/* Rating tooltip */}
719
+ </div>
720
+ </div>
721
+
722
+ {/* <div className="flex justify-between items-center gap-[10px]"> */}
723
+ {/* Duration */}
724
+ <div className="flex items-baseline relative text-[#464647]">
725
+ {/* {renderIcon("duration", "14px")} */}
726
+ <div
727
+ className={`w-[18px] h-auto mr-[4px] ${
728
+ isSoldOut ? "grayscale" : ""
729
+ }`}
730
+ >
731
+ {renderIcon("hours", "14px")}
732
+ </div>
733
+ <div
734
+ className={`cursor-default group text-[13.33px] ${
735
+ isSoldOut ? "text-[#c0c0c0]" : ""
736
+ }`}
737
+ >
738
+ {serviceItem.duration} {translation.hours}
739
+ <div
740
+ className=" hidden group-hover:block absolute top-[24px] left-1/2 -translate-x-1/2 text-white p-3 rounded-[14px] whitespace-normal z-10 mt-2.5 w-[230px] text-center break-normal shadow-service"
741
+ style={{ backgroundColor: colors.tooltipColor }}
742
+ >
743
+ {/* Tooltip arrow */}
744
+ <div
745
+ className="tooltip-arrow absolute -top-[7px] left-1/2 -translate-x-1/2 w-0 h-0 border-l-8 border-r-8 border-b-8 border-l-transparent border-r-transparent"
746
+ style={{ borderBottomColor: colors.tooltipColor }}
747
+ ></div>
748
+ Duración estimada del viaje
749
+ </div>
750
+ </div>
751
+ </div>
752
+
753
+ {serviceItem.pet_seat_info &&
754
+ Object.keys(serviceItem.pet_seat_info).length > 0 ? (
755
+ <div className="flex items-center">
756
+ <div className={`relative group cursor-default `}>
757
+ <div className="flex items-center">
758
+ <div className={`mr-[5px] ${isSoldOut ? "grayscale" : ""}`}>
759
+ <LottiePlayer
760
+ animationData={serviceItem.icons.petFriendlyAnim}
761
+ width="20px"
762
+ height="20px"
763
+ />
764
+ </div>
765
+ <div className="h-auto mr-[4px] text-[13px] text-[#464647]">
766
+ <span>{translation?.petFriendly}</span>
767
+ </div>
768
+ </div>
769
+ <div
770
+ className=" hidden group-hover:block absolute top-[24px] left-1/2 -translate-x-1/2 text-white p-3 rounded-[14px] whitespace-normal z-10 mt-2.5 w-[230px] text-center break-normal shadow-service"
771
+ style={{ backgroundColor: colors?.tooltipColor }}
772
+ >
773
+ <div
774
+ className="tooltip-arrow absolute text-[13.33px] -top-[7px] left-1/2 -translate-x-1/2 w-0 h-0 border-l-8 border-r-8 border-b-8 border-l-transparent border-r-transparent"
775
+ style={{ borderBottomColor: colors?.tooltipColor }}
776
+ ></div>
777
+ Este servicio incluye asientos para mascotas.
778
+ </div>
779
+ </div>
780
+ </div>
781
+ ) : null}
782
+
783
+ {/* Flexible ticket */}
784
+ {serviceItem.is_change_ticket && (
785
+ <div className="flex items-center">
786
+ <div className="relative group cursor-default">
787
+ <div className="flex items-center">
788
+ <div className={`mr-[5px] ${isSoldOut ? "grayscale" : ""}`}>
789
+ <LottiePlayer
790
+ animationData={serviceItem.icons.flexibleAnim}
791
+ width="20px"
792
+ height="20px"
793
+ />
794
+ </div>
795
+ <div className="h-auto mr-[4px] text-[13px] text-[#464647]">
796
+ <span>{translation?.flexible}</span>
797
+ </div>
798
+ </div>
799
+ <div
800
+ className="hidden group-hover:block absolute top-[24px] left-1/2 -translate-x-1/2 text-white p-3 rounded-[14px] whitespace-normal z-10 mt-2.5 w-[230px] text-center break-normal shadow-service text-[13.33px]"
801
+ style={{ backgroundColor: colors.tooltipColor }}
802
+ >
803
+ {/* Tooltip arrow */}
804
+ <div
805
+ className="tooltip-arrow absolute -top-[7px] left-1/2 -translate-x-1/2 w-0 h-0 border-l-8 border-r-8 border-b-8 border-l-transparent border-r-transparent "
806
+ style={{ borderBottomColor: colors.tooltipColor }}
807
+ ></div>
808
+ Esta empresa permite cambios sin costo hasta (
809
+ {serviceItem?.change_ticket_hours ?? 6}) horas antes del
810
+ viaje.
811
+ </div>
812
+ </div>
813
+ </div>
814
+ )}
815
+
816
+ {/* Amenities */}
817
+ <div className="flex items-center">
818
+ <div>
819
+ {serviceItem?.is_tracking_enabled && (
820
+ <div className="flex items-center mr-[10px]">
821
+ <div
822
+ className={`h-auto mr-[4px] text-[13px] text-[#464647] ${
823
+ isSoldOut ? "grayscale" : ""
824
+ }`}
825
+ >
826
+ <LottiePlayer
827
+ animationData={serviceItem.icons.locationAnim}
828
+ width="20px"
829
+ height="20px"
830
+ />
831
+ </div>
832
+ </div>
833
+ )}
834
+ </div>
835
+ <div>
836
+ {metaData && serviceItem.operator_details[4] && (
837
+ <div className="flex items-center gap-[6px]">
838
+ {/* Show first three amenities */}
839
+ {(serviceItem.operator_details[3]?.includes("18")
840
+ ? [...serviceItem.operator_details[4], "18"]
841
+ : serviceItem.operator_details[4]
842
+ ).map((val, key) => {
843
+ return (key < 3 &&
844
+ metaData?.amenities[val]
845
+ ?.split(".")[0]
846
+ ?.toUpperCase() !== "WATER") ||
847
+ val === "18" ? (
848
+ <div key={key} className="relative mr-2 cursor-pointer">
849
+ <div className="group">
850
+ <div className={`${isSoldOut ? "grayscale" : ""}`}>
851
+ <SvgAmenities
852
+ moreAnemities={false}
853
+ name={metaData.amenities[val]
854
+ ?.split(".")[0]
855
+ .toLowerCase()}
856
+ />
857
+ </div>
858
+ <div
859
+ className="hidden group-hover:block absolute top-[24px] left-1/2 -translate-x-1/2 text-white p-3 rounded-[14px] whitespace-nowrap z-10 mt-2.5 text-center shadow-service text-[13.33px]"
860
+ style={{ backgroundColor: colors.tooltipColor }}
861
+ >
862
+ {/* Tooltip arrow */}
863
+ <div
864
+ className="tooltip-arrow absolute -top-[7px] left-1/2 -translate-x-1/2 w-0 h-0 border-l-8 border-r-8 border-b-8 border-l-transparent border-r-transparent"
865
+ style={{
866
+ borderBottomColor: colors.tooltipColor,
867
+ }}
868
+ ></div>
869
+ {getAmenityName(
870
+ metaData.amenities[val]
871
+ ?.split(".")[0]
872
+ .split("_")
873
+ .join(" ")
874
+ )}
875
+ </div>
876
+ </div>
877
+ </div>
878
+ ) : null;
879
+ })}
880
+
881
+ {/* Plus icon for more amenities */}
882
+ {serviceItem.operator_details[4]?.length > 3 && (
883
+ <div className="relative ml-1 cursor-pointer">
884
+ <div className="w-[16px] h-[16px] bg-gray-200 rounded-full flex items-center justify-center text-sm font-bold ml-[5px] group">
885
+ <img
886
+ src={
887
+ serviceItem.icons?.plus ||
888
+ "/images/icons/amenities/icon_plus.svg"
889
+ }
890
+ className="w-[16px] h-[16px]"
891
+ alt="plus"
892
+ />
893
+ <div className="hidden group-hover:block absolute -top-[13px] left-[40px] z-20 pl-[10px]">
894
+ <div
895
+ className="flex flex-col gap-[10px] p-3 rounded-[8px] shadow-md animate-fadeIn relative"
896
+ style={{ backgroundColor: colors.tooltipColor }}
897
+ >
898
+ {/* Additional amenities */}
899
+ {metaData &&
900
+ serviceItem.operator_details[4]?.map(
901
+ (val, key) => {
902
+ const exceptions = [
903
+ 1, 2, 5, 7, 8, 9, 12, 13, 14, 15,
904
+ ];
905
+ return exceptions.includes(
906
+ key
907
+ ) ? null : key >= 3 &&
908
+ metaData.amenities[val]
909
+ ?.split(".")[0]
910
+ ?.toUpperCase() !== "WATER" ? (
911
+ <div
912
+ key={key}
913
+ className="flex items-center gap-[5px] whitespace-nowrap text-[13.33px]"
914
+ >
915
+ <div
916
+ className={`${
917
+ isSoldOut ? "grayscale" : ""
918
+ }`}
919
+ >
920
+ <SvgAmenities
921
+ moreAnemities={true}
922
+ name={metaData.amenities[val]
923
+ ?.split(".")[0]
924
+ ?.toUpperCase()}
925
+ color="white"
926
+ />
927
+ </div>
928
+ <span className="text-white text-xs">
929
+ {getAmenityName(
930
+ metaData.amenities[val]
931
+ ?.split(".")[0]
932
+ ?.split("_")
933
+ ?.join(" ")
934
+ )}
935
+ </span>
936
+ </div>
937
+ ) : null;
938
+ }
939
+ )}
940
+ {/* Tooltip arrow */}
941
+ <div
942
+ className="tooltip-arrow absolute -left-[6px] top-[15px] w-0 h-0 border-t-8 border-b-8 border-r-8 border-t-transparent border-b-transparent "
943
+ style={{
944
+ borderRightColor: colors.tooltipColor,
945
+ }}
946
+ ></div>
947
+ </div>
948
+ </div>
949
+ </div>
950
+ </div>
951
+ )}
952
+ </div>
953
+ )}
954
+ </div>
955
+ {/* </div> */}
956
+ </div>
957
+ </div>
958
+ </div>
959
+ </div>
960
+
961
+ {children}
962
+ {/* Bottom discount banner */}
963
+ {serviceItem?.offer_text && (
964
+ <div
965
+ 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]`}
966
+ style={{ backgroundColor: isSoldOut ? "" : colors?.bottomStripColor }}
967
+ >
968
+ <LottiePlayer
969
+ animationData={serviceItem.icons.promoAnim}
970
+ width="18px"
971
+ height="18px"
972
+ />
973
+ <span className="ml-[10px]">{serviceItem?.offer_text}</span>
974
+ </div>
975
+ )}
976
+
977
+ <div className="absolute -top-[17px] left-0 w-full flex items-center justify-end gap-[12px] pr-[20px] z-10 ">
978
+ {showTopLabel && (
979
+ <div
980
+ className={`flex items-center gap-[10px] py-[8px] px-[20px] rounded-[38px] text-[13.33px] z-20 ${
981
+ isSoldOut ? "bg-[#ddd]" : ``
982
+ }`}
983
+ style={{ backgroundColor: !isSoldOut && colors.ratingBottomColor }}
984
+ >
985
+ <div className={isSoldOut ? "grayscale" : ""}>
986
+ <LottiePlayer
987
+ animationData={serviceItem.icons.priorityStageAnim}
988
+ width="20px"
989
+ height="20px"
990
+ />
991
+ </div>
992
+ <div
993
+ className={
994
+ isSoldOut ? "text-white" : `text-[${colors.topLabelColor}]`
995
+ }
996
+ >
997
+ {showTopLabel}
998
+ </div>
999
+ </div>
1000
+ )}
1001
+ {serviceItem?.is_transpordo && (
1002
+ <div
1003
+ className={`flex items-center gap-[10px] py-[8px] text-white px-[20px] rounded-[38px] text-[13.33px] z-20 ${
1004
+ isSoldOut ? "bg-[#ddd]" : `bg-[${colors.tooltipColor}]`
1005
+ }`}
1006
+ >
1007
+ <LottiePlayer
1008
+ animationData={serviceItem.icons.connectingServiceIcon}
1009
+ width="20px"
1010
+ height="20px"
1011
+ />
1012
+ <div>{"Conexión"}</div>
1013
+ </div>
1014
+ )}
1015
+ {serviceItem?.is_direct_trip && (
1016
+ <div
1017
+ className={`flex items-center gap-[10px] py-[8px] text-white px-[20px] rounded-[38px] text-[13.33px] z-20 ${
1018
+ isSoldOut ? "bg-[#ddd]" : `bg-[${colors.tooltipColor}]`
1019
+ }`}
1020
+ >
1021
+ <LottiePlayer
1022
+ animationData={serviceItem.icons.directoAnim}
1023
+ width="20px"
1024
+ height="20px"
1025
+ />
1026
+ <div>{translation?.directService}</div>
1027
+ </div>
1028
+ )}
1029
+ {serviceItem?.train_type_label === "Tren Express (Nuevo)" && (
1030
+ <div
1031
+ className={`flex items-center gap-[10px] py-[8px] text-white px-[20px] rounded-[38px] text-[13.33px] z-20 ${
1032
+ isSoldOut ? "bg-[#ddd]" : `bg-[${colors.tooltipColor}]`
1033
+ }`}
1034
+ >
1035
+ <LottiePlayer
1036
+ animationData={serviceItem.icons.directoAnim}
1037
+ width="20px"
1038
+ height="20px"
1039
+ />
1040
+ <div>{"Tren Express"}</div>
1041
+ </div>
1042
+ )}
1043
+ </div>
1044
+ </div>
1045
+ );
1046
+ }
1047
+
1048
+ export default ServiceItemPB;