kupos-ui-components-lib 9.9.0 → 9.9.2

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 (36) 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 +2 -2
  4. package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
  5. package/dist/components/ServiceItem/ServiceItemDesktop.js +29 -12
  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 -1
  9. package/dist/components/ServiceItem/types.d.ts +27 -2
  10. package/dist/styles.css +215 -3
  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 +22 -41
  14. package/dist/ui/FeatureServiceUI/FeatureServiceUi.js +24 -44
  15. package/dist/ui/OfferBanner.d.ts +2 -0
  16. package/dist/ui/OfferBanner.js +19 -14
  17. package/dist/ui/SeatSection/SeatSection.js +3 -3
  18. package/dist/utils/CommonService.d.ts +3 -0
  19. package/dist/utils/CommonService.js +18 -1
  20. package/package.json +1 -1
  21. package/src/KuposUIComponent.tsx +3 -0
  22. package/src/assets/images/anims/service_list/flame_anim.json +1 -0
  23. package/src/assets/images/anims/service_list/thunder_icon.json +1 -0
  24. package/src/assets/images/anims/service_list/users_anim.json +1 -0
  25. package/src/components/ServiceItem/PeruServiceItemDesktop.tsx +35 -24
  26. package/src/components/ServiceItem/ServiceItemDesktop.tsx +65 -10
  27. package/src/components/ServiceItem/ServiceItemMobile.tsx +387 -288
  28. package/src/components/ServiceItem/mobileTypes.ts +50 -7
  29. package/src/components/ServiceItem/types.ts +39 -13
  30. package/src/styles.css +15 -0
  31. package/src/ui/ExpendedDropDown/ExpandedDropdown.tsx +2 -4
  32. package/src/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.tsx +575 -0
  33. package/src/ui/FeatureServiceUI/FeatureServiceUi.tsx +610 -0
  34. package/src/ui/OfferBanner.tsx +31 -10
  35. package/src/ui/SeatSection/SeatSection.tsx +3 -3
  36. package/src/utils/CommonService.ts +26 -1
@@ -0,0 +1,575 @@
1
+ import React from "react";
2
+ import LottiePlayer from "../../assets/LottiePlayer";
3
+ import commonService from "../../utils/CommonService";
4
+ import flameAnimation from "../../assets/images/anims/service_list/flame_anim.json";
5
+
6
+ const TIME_SLOTS = [
7
+ "Entre 07:00 AM y 10:00 AM",
8
+ "Entre 11:00 AM y 14:00 AM",
9
+ "Entre 15:00 PM y 18:00 PM",
10
+ "Entre 19:00 PM y 22:00 PM",
11
+ ];
12
+
13
+ const HARDCODED_OPERATORS = [
14
+ {
15
+ logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
16
+ name: "Turbus",
17
+ time: "7:00 am",
18
+ seatsAvailable: "3 disponibles",
19
+ },
20
+ {
21
+ logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Pullman_Bus_logo.svg/320px-Pullman_Bus_logo.svg.png",
22
+ name: "Turbus",
23
+ time: "8:00 am",
24
+ seatsAvailable: "5 disponibles",
25
+ },
26
+ {
27
+ logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
28
+ name: "Turbus",
29
+ time: "9:00 am",
30
+ seatsAvailable: "3 disponibles",
31
+ },
32
+ ];
33
+
34
+ const FeatureServiceUiMobile = ({
35
+ serviceItem,
36
+ showTopLabel,
37
+ colors,
38
+ isSoldOut,
39
+ cityOrigin,
40
+ cityDestination,
41
+ renderIcon,
42
+ viewersConfig,
43
+ isFeatureDropDownExpand,
44
+ onToggleExpand,
45
+ ticketQuantity = 1,
46
+ onIncreaseTicketQuantity,
47
+ onDecreaseTicketQuantity,
48
+ onBookButtonPress,
49
+ selectedTimeSlot = TIME_SLOTS[0],
50
+ onTimeSlotChange,
51
+ isTimeDropdownOpen,
52
+ onTimeDropdownToggle,
53
+ wowDealData = undefined,
54
+ }) => {
55
+ // Use wow_deal data if available, otherwise fall back to serviceItem operators or hardcoded
56
+ const operators =
57
+ wowDealData?.services?.length > 0
58
+ ? wowDealData.services.slice(0, 3).map((service: any) => ({
59
+ logo: service.operator_logo_url,
60
+ name: service.operator_name,
61
+ time: service.departure_time,
62
+ seatsAvailable: `${service.available_seats} disponibles`,
63
+ }))
64
+ : serviceItem?.operators?.length > 0
65
+ ? serviceItem.operators
66
+ : HARDCODED_OPERATORS;
67
+
68
+ // Use wow_deal pricing if available
69
+ const finalPrice = wowDealData?.final_price || 4000;
70
+ const originalPrice = wowDealData?.original_price || 10000;
71
+ const savingsPercent = wowDealData?.savings_percent || 60;
72
+ const operatorsCompetingCount = wowDealData?.operators_competing_count || 3;
73
+ const maxSeatsPerBooking = wowDealData?.max_seats_per_booking || 4;
74
+ const expiresAt = wowDealData?.expires_at;
75
+ const isPostPaymentAssignment = wowDealData?.is_post_payment_assignment;
76
+ const dealWindowFrom = wowDealData?.deal_window_from || "07:00";
77
+ const dealWindowTo = wowDealData?.deal_window_to || "10:00";
78
+ const travelDate = wowDealData?.travel_date;
79
+
80
+ // Calculate countdown seconds from expires_at ISO timestamp
81
+ const getCountdownSeconds = () => {
82
+ if (!expiresAt) return 599;
83
+ const expires = new Date(expiresAt).getTime();
84
+ const now = Date.now();
85
+ const seconds = Math.max(0, Math.floor((expires - now) / 1000));
86
+ return seconds;
87
+ };
88
+
89
+ // Generate time slot from deal window
90
+ const dynamicTimeSlot = `Entre ${dealWindowFrom} y ${dealWindowTo}`;
91
+ const displayTimeSlot = selectedTimeSlot || dynamicTimeSlot;
92
+
93
+ const isItemExpanded =
94
+ serviceItem.id === isFeatureDropDownExpand ||
95
+ isFeatureDropDownExpand === true;
96
+ const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
97
+
98
+ const canDecreaseTicketQuantity = ticketQuantity > 1;
99
+
100
+ const departures = wowDealData?.services
101
+ ?.map((s) => s.departure_time)
102
+ ?.filter(Boolean);
103
+
104
+ let departureRange = `Entre ${dealWindowFrom} y ${dealWindowTo}`;
105
+
106
+ if (departures?.length) {
107
+ const sorted = [...departures].sort((a, b) => {
108
+ const [ah, am] = a.split(":").map(Number);
109
+ const [bh, bm] = b.split(":").map(Number);
110
+ return ah * 60 + am - (bh * 60 + bm);
111
+ });
112
+
113
+ departureRange = `Entre ${sorted[0]} y ${sorted[sorted.length - 1]}`;
114
+ }
115
+
116
+ const HOW_IT_WORKS_STEPS = [
117
+ {
118
+ icon: "flexible",
119
+ name: "Salida flexible",
120
+ text: `Viajas en un horario entre las ${dealWindowFrom} y las ${dealWindowTo} del día elegido.`,
121
+ },
122
+ {
123
+ icon: "bus",
124
+ name: "Empresa asignada",
125
+ text: isPostPaymentAssignment
126
+ ? "Empresa y hora a confirmar luego del pago."
127
+ : "Una de las empresas confirmará tu viaje al instante tras el pago.",
128
+ },
129
+ {
130
+ icon: "price",
131
+ name: "Precio garantizado",
132
+ text: "Mejor precio garantizado. Sin cambios ni cancelación.",
133
+ },
134
+ {
135
+ icon: "ticket",
136
+ name: "¡Listo!",
137
+ text: "Recibe todos los detalles de tu viaje al instante tras la compra.",
138
+ },
139
+ ];
140
+
141
+ const FeatureStepIcon = ({ icon }) => {
142
+ switch (icon) {
143
+ case "flexible":
144
+ return renderIcon("flexibleIcon", "20px");
145
+ case "bus":
146
+ return renderIcon("empressaIcon", "20px");
147
+ case "price":
148
+ return renderIcon("precioIcon", "20px");
149
+ default:
150
+ return renderIcon("listoIcon", "20px");
151
+ }
152
+ };
153
+
154
+ return (
155
+ <div
156
+ // ${
157
+ // serviceItem.offer_text ? "mb-[55px]" : "mb-[10px]"
158
+ // }
159
+ className={`relative mb-[10px]
160
+ ${serviceItem?.is_direct_trip ||
161
+ serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
162
+ showTopLabel
163
+ ? "mt-[24px]"
164
+ : "mt-[20px]"
165
+ }`}
166
+ >
167
+ <div
168
+ // className="shadow-service"
169
+ style={{
170
+ border: "1px solid #c0c0c0",
171
+ padding: "12px",
172
+ borderRadius: "14px",
173
+ }}
174
+ >
175
+ <div className="flex flex-col items-center px-[12px] pb-[8px] text-[13.33px] gap-[8px]">
176
+ <div className="flex items-center gap-[10px] mt-[16px]">
177
+ <span>Salida flexible</span>
178
+ <div
179
+ className="bold-text font-[9px]"
180
+ style={{
181
+ backgroundColor: "#FF5C60",
182
+ padding: "4px 8px",
183
+ borderRadius: "10px",
184
+ color: "#fff",
185
+ animation: "pulse-zoom 2s ease-in-out infinite",
186
+ whiteSpace: "nowrap",
187
+ fontSize: "10px",
188
+ }}
189
+ >
190
+ <span>AHORRAS {savingsPercent}%</span>
191
+ </div>
192
+ </div>
193
+ </div>
194
+ <div
195
+ id={`service-card-${serviceItem.id}`}
196
+ className="bg-[#0C1421] text-white mx-auto relative rounded-[14px] p-[14px] text-[13.33px]"
197
+ >
198
+ <div className="flex flex-col gap-[10px]">
199
+ <div className=" text-[white]">
200
+ <div className="flex flex-col gap-[10px] relative">
201
+ <div className="flex items-center gap-[6px]">
202
+ <img
203
+ src={serviceItem.icons?.whiteOrigin}
204
+ alt="origin"
205
+ className={`w-[13px] h-[13px] shrink-0 ${isSoldOut ? "grayscale" : ""
206
+ }`}
207
+ />
208
+
209
+ <span className="text-[14px] bold-text">
210
+ {cityOrigin?.label.split(",")[0]}
211
+ </span>
212
+
213
+ <span className="mx-[6px] text-[14px] bold-text">→</span>
214
+
215
+ <img
216
+ src={serviceItem.icons?.whiteDestination}
217
+ alt="destination"
218
+ className={`w-[13px] h-[13px] shrink-0 ${isSoldOut ? "grayscale" : ""
219
+ }`}
220
+ style={{ opacity: isSoldOut ? 0.5 : 1 }}
221
+ />
222
+
223
+ <span className="text-[14px] bold-text">
224
+ {cityDestination?.label.split(",")[0]}
225
+ </span>
226
+ </div>
227
+
228
+ <div className="flex items-center gap-[6px]">
229
+ <div className="bold-text text-[12px] text-white">
230
+ {departureRange}
231
+ </div>
232
+ {/* <div
233
+ className="kupos-time-dd relative"
234
+ tabIndex={0}
235
+ onBlur={(e) => {
236
+ if (!e.currentTarget.contains(e.relatedTarget as Node)) {
237
+ onTimeDropdownToggle?.(null);
238
+ }
239
+ }}
240
+ style={{ outline: "none" }}
241
+ >
242
+ <button
243
+ type="button"
244
+ onClick={() =>
245
+ onTimeDropdownToggle?.(
246
+ isThisTimeDropdownOpen ? null : serviceItem.id,
247
+ )
248
+ }
249
+ className="flex cursor-pointer select-none items-center gap-[6px] border-none bg-transparent p-0 bold-text text-[13px] text-[white]"
250
+ >
251
+ <span>{displayTimeSlot}</span>
252
+ <img
253
+ src={serviceItem?.icons?.downArrow}
254
+ alt="down arrow"
255
+ className={`kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`}
256
+ style={{
257
+ width: "12px",
258
+ height: "8px",
259
+ filter: "brightness(0) invert(1)",
260
+ }}
261
+ />
262
+ </button>
263
+ {isThisTimeDropdownOpen && (
264
+ <>
265
+ <div
266
+ className="absolute left-0 top-[calc(100%+10px)]"
267
+ style={{
268
+ zIndex: 20,
269
+ backgroundColor: "#fff",
270
+ borderRadius: "14px",
271
+ minWidth: "190px",
272
+ boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
273
+ overflow: "hidden",
274
+ padding: "6px 0",
275
+ }}
276
+ >
277
+ {TIME_SLOTS.map((slot) => {
278
+ const isActive = slot === selectedTimeSlot;
279
+ return (
280
+ <button
281
+ key={slot}
282
+ type="button"
283
+ onClick={() => {
284
+ onTimeSlotChange?.(slot);
285
+ onTimeDropdownToggle?.(null);
286
+ }}
287
+ className={`flex w-full cursor-pointer items-center gap-[10px] border-none px-[12px] py-[9px] text-left text-[13px] ${isActive ? "bg-[#FF5C60] font-bold text-[white]" : "bg-transparent font-normal text-[#1a1a1a]"}`}
288
+ >
289
+ <span>{slot}</span>
290
+ </button>
291
+ );
292
+ })}
293
+ </div>
294
+ </>
295
+ )}
296
+ </div> */}
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ <div className="border-t border-[#363c48] my-[8px]" />
302
+
303
+ <div>
304
+ <span
305
+ className="block w-full text-[14px] bold-text text-[white] mb-[10px]"
306
+ style={{ textAlign: "center" }}
307
+ >
308
+ {operatorsCompetingCount} operadores compitiendo <br />
309
+ por tu compra
310
+ </span>
311
+ <div
312
+ className="flex gap-[8px] text-[white]"
313
+ style={{ width: "100%" }}
314
+ >
315
+ {operators.map((op, idx) => (
316
+ <div
317
+ key={idx}
318
+ className="flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]"
319
+ style={{
320
+ flex: 1,
321
+ minWidth: 0,
322
+ height: "100px",
323
+ border: "1px solid #363c48",
324
+ backgroundColor: "#1a202e",
325
+ padding: "14px 6px",
326
+ }}
327
+ >
328
+ <img
329
+ src={op.logo}
330
+ alt={op.name}
331
+ onError={(e) => {
332
+ e.currentTarget.src = serviceItem?.operator_details?.[0] || "/images/service-list/bus-icon.svg";
333
+ }}
334
+ className={`h-[24px] max-w-full object-contain ${isSoldOut ? "grayscale" : ""
335
+ }`}
336
+ />
337
+ <span className="text-[12px] truncate max-w-full text-center ">
338
+ {op.name}
339
+ </span>
340
+ {/* <div className="bg-[#FF8F45] text-[12px] font-bold px-[10px] py-[4px] rounded-[4px] bold-text whitespace-nowrap">
341
+ <span>{op?.time}</span>
342
+ </div> */}
343
+ <span className="text-[11px] whitespace-nowrap">
344
+ {op?.seatsAvailable}
345
+ </span>
346
+ </div>
347
+ ))}
348
+ </div>
349
+
350
+ <div
351
+ className="flex w-full items-center justify-center gap-[6px] text-[12px] mt-[12px]"
352
+ style={{
353
+ padding: "4px 14px",
354
+ }}
355
+ >
356
+ <LottiePlayer
357
+ animationData={serviceItem.icons.personsAnim}
358
+ width="18px"
359
+ height="18px"
360
+ />
361
+ <span>
362
+ <span className="bold-text text-[white]">
363
+ {" "}
364
+ <span
365
+ className="bold-text"
366
+ ref={(node) =>
367
+ commonService.startViewerCount(node, viewersConfig)
368
+ }
369
+ style={{
370
+ fontVariantNumeric: "tabular-nums",
371
+ color: "#FF5C60",
372
+ }}
373
+ />{" "}
374
+ </span>{" "}
375
+ <span style={{ color: "#FF5C60" }}>viendo</span>
376
+ <span className="text-[white]"> |</span>{" "}
377
+ <span
378
+ className="bold-text text-[white]"
379
+ ref={(node) =>
380
+ commonService.startComprandoCount(node, 4, 16)
381
+ }
382
+ style={{ fontVariantNumeric: "tabular-nums" }}
383
+ />{" "}
384
+ <span className="text-[white]">han comprado</span>
385
+ </span>
386
+ </div>
387
+
388
+ <div className="mt-[10px] flex flex-col items-center gap-[8px]">
389
+ <span className="text-[12px] text-[white]">
390
+ ¿Cuántos pasajes quieres?
391
+ </span>
392
+ <div
393
+ className="flex w-[50%] items-center justify-between"
394
+ style={{
395
+ border: "1px solid #363c48",
396
+ backgroundColor: "#1a202e",
397
+ padding: "4px 14px",
398
+ borderRadius: "14px",
399
+ }}
400
+ >
401
+ <button
402
+ type="button"
403
+ aria-label="Disminuir pasajes"
404
+ disabled={!canDecreaseTicketQuantity}
405
+ onClick={() => onDecreaseTicketQuantity?.(serviceItem)}
406
+ className={`flex h-[26px] w-[26px] items-center justify-center rounded-full border-none text-[16px] leading-none text-[white] ${canDecreaseTicketQuantity
407
+ ? "cursor-pointer bg-[#2d374d]"
408
+ : "cursor-not-allowed bg-[#222b3d] opacity-50"
409
+ }`}
410
+ >
411
+ -
412
+ </button>
413
+ <span className="bold-text text-[14px] text-[white]">
414
+ {ticketQuantity}
415
+ </span>
416
+ <button
417
+ type="button"
418
+ aria-label="Aumentar pasajes"
419
+ onClick={() => onIncreaseTicketQuantity?.(serviceItem)}
420
+ className="flex h-[26px] w-[26px] cursor-pointer items-center justify-center rounded-full border-none bg-[#2d374d] text-[16px] leading-none text-[white]"
421
+ >
422
+ +
423
+ </button>
424
+ </div>
425
+ </div>
426
+
427
+ <div
428
+ className="mt-[10px] flex justify-between items-center rounded-[14px]"
429
+ style={{
430
+ // height: "80px",
431
+ border: "1px solid #363c48",
432
+ backgroundColor: "#1a202e",
433
+ padding: "14px 10px",
434
+ }}
435
+ >
436
+ <div className="flex flex-col">
437
+ <span
438
+ className="text-[18px] font-normal leading-[20px] text-[#9f9f9f] relative"
439
+ style={{ position: "relative" }}
440
+ >
441
+ {`$${((originalPrice * ticketQuantity)).toLocaleString()}`}
442
+ <span
443
+ style={{
444
+ position: "absolute",
445
+ left: "-2px",
446
+ top: "50%",
447
+ width: "calc(80% + 4px)",
448
+ height: "1px",
449
+
450
+ backgroundColor: "#FF5C60",
451
+
452
+ transform: "rotate(-10deg)",
453
+ transformOrigin: "center",
454
+ }}
455
+ />
456
+ </span>
457
+ <span className="text-[white] bold-text text-[24px] leading-none mt-[4px]">
458
+ {`$${(finalPrice * ticketQuantity).toLocaleString()}`}
459
+ </span>
460
+ </div>
461
+ <span
462
+ className="text-[#FF8F45] bold-text text-[22px] leading-tight"
463
+ style={{
464
+ animation: "pulse-zoom 2s ease-in-out infinite",
465
+ whiteSpace: "nowrap",
466
+ }}
467
+ >
468
+ {savingsPercent}% OFF
469
+ </span>
470
+ </div>
471
+
472
+ <button
473
+ type="button"
474
+ onClick={onBookButtonPress}
475
+ className="flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-[white] bold-text text-[13px] mt-[10px] justify-center border-none cursor-pointer"
476
+ style={{
477
+ backgroundColor: "#FF5C60",
478
+ whiteSpace: "nowrap",
479
+ width: "100%",
480
+ }}
481
+ >
482
+ <LottiePlayer
483
+ animationData={serviceItem.icons.thunderAnim}
484
+ width="16px"
485
+ height="16px"
486
+ />
487
+ <span className="whitespace-nowrap">¡Lo quiero!</span>
488
+ </button>
489
+
490
+ <div
491
+ className="flex justify-end mt-[10px]"
492
+ onClick={onToggleExpand}
493
+ >
494
+ <img
495
+ src={serviceItem?.icons?.downArrow}
496
+ alt="down arrow"
497
+ className={`transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`}
498
+ style={{
499
+ width: "14px",
500
+ height: "8px",
501
+ filter: "brightness(0) invert(1)",
502
+ marginRight: "2px",
503
+ }}
504
+ />
505
+ </div>
506
+ </div>
507
+ </div>
508
+ </div>
509
+
510
+ {isItemExpanded && (
511
+ <div
512
+ className="flex flex-col gap-[10px]"
513
+ style={{
514
+ opacity: isItemExpanded ? 1 : 0,
515
+ transition: "opacity 250ms ease-in-out",
516
+ }}
517
+ >
518
+ <div
519
+ className={` text-[12px] ${isItemExpanded ? "pt-[14px] pb-[6px]" : "py-0"
520
+ }`}
521
+ style={{ transition: "padding 300ms ease-in-out" }}
522
+ >
523
+ <span className="bold-text">¿Cómo funciona?</span>
524
+
525
+ <div className="flex flex-col">
526
+ {HOW_IT_WORKS_STEPS.map((step) => (
527
+ <div className="flex items-start gap-[8px] mt-[10px]">
528
+ <div>
529
+ <FeatureStepIcon icon={step.icon} />
530
+ </div>
531
+ <div className="flex flex-col">
532
+ <span className="bold-text">{step.name}</span>
533
+ <span>{step.text}</span>
534
+ </div>
535
+ </div>
536
+ ))}
537
+ </div>
538
+ </div>
539
+ </div>
540
+ )}
541
+ </div>
542
+
543
+ {/* TOP BADGE — "Remate | Termina en 09:55 min" hardcoded, no countdown hook */}
544
+ <div className="absolute -top-[13px] left-0 w-full flex items-center justify-center gap-[12px] z-10 ">
545
+ <div className="flex items-center gap-[6px] py-[5px] px-[14px] rounded-[38px] text-[12.5px] bg-[#FF8F45] text-white whitespace-nowrap">
546
+ <span className="flex items-center">
547
+ <div className="mb-[2px]">
548
+ <LottiePlayer
549
+ animationData={flameAnimation}
550
+ width="16px"
551
+ height="16px"
552
+ />
553
+ </div>
554
+ <span className="bold-text text-[white] ml-[3px]">
555
+ Remate&nbsp;
556
+ </span>{" "}
557
+ <span className="text-[white]">términa en</span>{" "}
558
+ <span
559
+ className="bold-text text-end"
560
+ ref={(node) => commonService.startCountdown(node, getCountdownSeconds())}
561
+ style={{
562
+ fontVariantNumeric: "tabular-nums",
563
+ display: "inline-block",
564
+ color: "#fff",
565
+ minWidth: "40px",
566
+ }}
567
+ />
568
+ </span>
569
+ </div>
570
+ </div>
571
+ </div>
572
+ );
573
+ };
574
+
575
+ export default FeatureServiceUiMobile;