kupos-ui-components-lib 9.8.0 → 9.8.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.
@@ -1,529 +0,0 @@
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: "Pullmanbus",
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: "Expreso Santa C...",
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
- }) => {
54
- const operators =
55
- serviceItem?.operators?.length > 0
56
- ? serviceItem.operators
57
- : HARDCODED_OPERATORS;
58
-
59
- const isItemExpanded =
60
- serviceItem.id === isFeatureDropDownExpand ||
61
- isFeatureDropDownExpand === true;
62
- const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
63
-
64
- const canDecreaseTicketQuantity = ticketQuantity > 1;
65
-
66
- const HOW_IT_WORKS_STEPS = [
67
- {
68
- icon: "flexible",
69
- name: "Salida flexible",
70
- text: "Viajas en un horario entre las 07:00 y las 10:00 AM del día elegido.",
71
- },
72
- {
73
- icon: "bus",
74
- name: "Empresa asignada",
75
- text: "Una de las empresas confirmará tu viaje al instante tras el pago.",
76
- },
77
- {
78
- icon: "price",
79
- name: "Precio garantizado",
80
- text: "Mejor precio garantizado. Sin cambios ni cancelación.",
81
- },
82
- {
83
- icon: "ticket",
84
- name: "¡Listo!",
85
- text: "Recibe todos los detalles de tu viaje al instante tras la compra.",
86
- },
87
- ];
88
-
89
- const FeatureStepIcon = ({ icon }) => {
90
- switch (icon) {
91
- case "flexible":
92
- return renderIcon("flexibleIcon", "20px");
93
- case "bus":
94
- return renderIcon("empressaIcon", "20px");
95
- case "price":
96
- return renderIcon("precioIcon", "20px");
97
- default:
98
- return renderIcon("listoIcon", "20px");
99
- }
100
- };
101
-
102
- return (
103
- <div
104
- // ${
105
- // serviceItem.offer_text ? "mb-[55px]" : "mb-[10px]"
106
- // }
107
- className={`relative mb-[10px]
108
- ${
109
- serviceItem?.is_direct_trip ||
110
- serviceItem?.train_type_label === "Tren Express (Nuevo)" ||
111
- showTopLabel
112
- ? "mt-[24px]"
113
- : "mt-[20px]"
114
- }`}
115
- >
116
- <div
117
- // className="shadow-service"
118
- style={{
119
- border: "1px solid #c0c0c0",
120
- padding: "12px",
121
- borderRadius: "14px",
122
- }}
123
- >
124
- <div className="flex flex-col items-center px-[12px] pb-[8px] text-[13.33px] gap-[8px]">
125
- <div className="flex items-center gap-[10px] mt-[16px]">
126
- <span>Salida flexible</span>
127
- <div
128
- className="bold-text font-[9px]"
129
- style={{
130
- backgroundColor: "#FF5C60",
131
- padding: "1px 8px",
132
- borderRadius: "4px",
133
- color: "#fff",
134
- animation: "pulse-zoom 2s ease-in-out infinite",
135
- whiteSpace: "nowrap",
136
- }}
137
- >
138
- <span>AHORRAS 60%</span>
139
- </div>
140
- </div>
141
- </div>
142
- <div
143
- id={`service-card-${serviceItem.id}`}
144
- className="bg-[#0C1421] text-white mx-auto relative rounded-[14px] p-[14px] text-[13.33px]"
145
- >
146
- <div className="flex flex-col gap-[10px]">
147
- <div className=" text-[white]">
148
- <div className="flex flex-col gap-[10px] relative">
149
- <div className="flex items-center gap-[6px]">
150
- <img
151
- src={serviceItem.icons?.whiteOrigin}
152
- alt="origin"
153
- className={`w-[14px] h-[14px] shrink-0 ${
154
- isSoldOut ? "grayscale" : ""
155
- }`}
156
- />
157
- <span className="text-[13px] bold-text">
158
- {cityOrigin?.label.split(",")[0]}
159
- <span className="mx-[10px]">→</span>
160
- {cityDestination?.label.split(",")[0]}
161
- </span>
162
- </div>
163
- <div
164
- style={{
165
- width: "1px",
166
- flex: 1,
167
- backgroundColor: "#fff",
168
- margin: "3px 0",
169
- minHeight: "8px",
170
- position: "absolute",
171
- top: "13px",
172
- left: "7px",
173
- }}
174
- />
175
- <div className="flex items-center gap-[6px]">
176
- <img
177
- src={serviceItem.icons?.whiteDestination}
178
- alt="destination"
179
- className={`w-[14px] h-[14px] shrink-0 ${
180
- isSoldOut ? "grayscale" : ""
181
- }`}
182
- style={{ opacity: isSoldOut ? 0.5 : 1 }}
183
- />
184
- <div
185
- className="kupos-time-dd relative"
186
- tabIndex={0}
187
- onBlur={(e) => {
188
- if (!e.currentTarget.contains(e.relatedTarget as Node)) {
189
- onTimeDropdownToggle?.(null);
190
- }
191
- }}
192
- style={{ outline: "none" }}
193
- >
194
- <button
195
- type="button"
196
- onClick={() =>
197
- onTimeDropdownToggle?.(
198
- isThisTimeDropdownOpen ? null : serviceItem.id,
199
- )
200
- }
201
- className="flex cursor-pointer select-none items-center gap-[6px] border-none bg-transparent p-0 bold-text text-[13px] text-[white]"
202
- >
203
- <span>{selectedTimeSlot}</span>
204
- <img
205
- src={serviceItem?.icons?.downArrow}
206
- alt="down arrow"
207
- className={`kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`}
208
- style={{
209
- width: "12px",
210
- height: "8px",
211
- filter: "brightness(0) invert(1)",
212
- }}
213
- />
214
- </button>
215
- {isThisTimeDropdownOpen && (
216
- <>
217
- <div
218
- className="absolute left-0 top-[calc(100%+10px)]"
219
- style={{
220
- zIndex: 20,
221
- backgroundColor: "#fff",
222
- borderRadius: "14px",
223
- minWidth: "248px",
224
- boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
225
- overflow: "hidden",
226
- padding: "6px 0",
227
- }}
228
- >
229
- {TIME_SLOTS.map((slot) => {
230
- const isActive = slot === selectedTimeSlot;
231
- return (
232
- <button
233
- key={slot}
234
- type="button"
235
- onClick={() => {
236
- onTimeSlotChange?.(slot);
237
- onTimeDropdownToggle?.(null);
238
- }}
239
- 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]"}`}
240
- >
241
- <span>{slot}</span>
242
- </button>
243
- );
244
- })}
245
- </div>
246
- </>
247
- )}
248
- </div>
249
- </div>
250
- </div>
251
- </div>
252
-
253
- <div className="border-t border-[#363c48] my-[8px]" />
254
-
255
- <div>
256
- <span
257
- className="block w-full text-[14px] bold-text text-[white] mb-[10px]"
258
- style={{ textAlign: "center" }}
259
- >
260
- 3 operadores compitiendo <br />
261
- por tu compra
262
- </span>
263
- <div
264
- className="flex gap-[8px] text-[white]"
265
- style={{ width: "100%" }}
266
- >
267
- {operators.map((op, idx) => (
268
- <div
269
- key={idx}
270
- className="flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]"
271
- style={{
272
- flex: 1,
273
- minWidth: 0,
274
- height: "100px",
275
- border: "1px solid #363c48",
276
- backgroundColor: "#1a202e",
277
- padding: "14px 6px",
278
- }}
279
- >
280
- <img
281
- src={serviceItem.operator_details[0]}
282
- alt={op.name}
283
- className={`h-[24px] max-w-full object-contain ${
284
- isSoldOut ? "grayscale" : ""
285
- }`}
286
- />
287
- <span className="text-[12px] truncate max-w-full text-center ">
288
- {serviceItem.operator_details[2]}
289
- </span>
290
- {/* <div className="bg-[#FF8F45] text-[12px] font-bold px-[10px] py-[4px] rounded-[4px] bold-text whitespace-nowrap">
291
- <span>{op?.time}</span>
292
- </div> */}
293
- <span className="text-[11px] whitespace-nowrap">
294
- {op?.seatsAvailable}
295
- </span>
296
- </div>
297
- ))}
298
- </div>
299
-
300
- <div
301
- className="flex w-full items-center justify-center gap-[6px] text-[12px] mt-[12px]"
302
- style={{
303
- border: "1px solid #363c48",
304
- backgroundColor: "#1a202e",
305
- padding: "4px 14px",
306
- borderRadius: "24px",
307
- }}
308
- >
309
- <LottiePlayer
310
- animationData={serviceItem.icons.personsAnim}
311
- width="18px"
312
- height="18px"
313
- />
314
- <span>
315
- <span className="bold-text text-[white]">
316
- {" "}
317
- <span
318
- className="bold-text"
319
- ref={(node) =>
320
- commonService.startViewerCount(node, viewersConfig)
321
- }
322
- style={{
323
- fontVariantNumeric: "tabular-nums",
324
- color: "#FF5C60",
325
- }}
326
- />{" "}
327
- </span>{" "}
328
- <span className="text-[white]">viendo |</span>{" "}
329
- <span
330
- className="bold-text text-[white]"
331
- ref={(node) =>
332
- commonService.startComprandoCount(node, 4, 16)
333
- }
334
- style={{ fontVariantNumeric: "tabular-nums" }}
335
- />{" "}
336
- <span className="text-[white]">han comprado</span>
337
- </span>
338
- </div>
339
-
340
- <div className="mt-[10px] flex flex-col items-center gap-[8px]">
341
- <span className="text-[12px] text-[white]">
342
- ¿Cuántos pasajes quieres?
343
- </span>
344
- <div
345
- className="flex w-[50%] items-center justify-between"
346
- style={{
347
- border: "1px solid #363c48",
348
- backgroundColor: "#1a202e",
349
- padding: "4px 14px",
350
- borderRadius: "14px",
351
- }}
352
- >
353
- <button
354
- type="button"
355
- aria-label="Disminuir pasajes"
356
- disabled={!canDecreaseTicketQuantity}
357
- onClick={() => onDecreaseTicketQuantity?.(serviceItem)}
358
- className={`flex h-[34px] w-[34px] items-center justify-center rounded-full border-none text-[25px] leading-none text-[white] ${
359
- canDecreaseTicketQuantity
360
- ? "cursor-pointer bg-[#2d374d]"
361
- : "cursor-not-allowed bg-[#222b3d] opacity-50"
362
- }`}
363
- >
364
- -
365
- </button>
366
- <span className="bold-text text-[20px] text-[white]">
367
- {ticketQuantity}
368
- </span>
369
- <button
370
- type="button"
371
- aria-label="Aumentar pasajes"
372
- onClick={() => onIncreaseTicketQuantity?.(serviceItem)}
373
- className="flex h-[34px] w-[34px] cursor-pointer items-center justify-center rounded-full border-none bg-[#2d374d] text-[25px] leading-none text-[white]"
374
- >
375
- +
376
- </button>
377
- </div>
378
- </div>
379
-
380
- <div
381
- className="mt-[10px] flex justify-between items-center rounded-[14px]"
382
- style={{
383
- // height: "80px",
384
- border: "1px solid #363c48",
385
- backgroundColor: "#1a202e",
386
- padding: "14px 10px",
387
- }}
388
- >
389
- <div className="flex flex-col">
390
- <span
391
- className="text-[20px] font-normal leading-[20px] text-[#9f9f9f] relative"
392
- style={{ position: "relative" }}
393
- >
394
- $10.000
395
- <span
396
- style={{
397
- position: "absolute",
398
- left: "-2px",
399
- top: "50%",
400
- width: "calc(80% + 4px)",
401
- height: "1px",
402
-
403
- backgroundColor: "#FF5C60",
404
-
405
- transform: "rotate(-10deg)",
406
- transformOrigin: "center",
407
- }}
408
- />
409
- </span>
410
- <span className="text-[white] bold-text text-[28px] leading-none mt-[4px]">
411
- {`$${(4000 * ticketQuantity).toLocaleString()}`}
412
- </span>
413
- </div>
414
- <span
415
- className="text-[#FF8F45] bold-text text-[26px] leading-tight"
416
- style={{
417
- animation: "pulse-zoom 2s ease-in-out infinite",
418
- whiteSpace: "nowrap",
419
- }}
420
- >
421
- 60% OFF
422
- </span>
423
- </div>
424
-
425
- <button
426
- type="button"
427
- onClick={onBookButtonPress}
428
- 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"
429
- style={{
430
- backgroundColor: "#FF5C60",
431
- animation: "pulse-zoom 2s ease-in-out infinite",
432
- whiteSpace: "nowrap",
433
- width: "100%",
434
- }}
435
- >
436
- <LottiePlayer
437
- animationData={serviceItem.icons.thunderAnim}
438
- width="18px"
439
- height="18px"
440
- />
441
- <span className="whitespace-nowrap">¡Lo quiero!</span>
442
- </button>
443
-
444
- <div
445
- className="flex justify-end mt-[10px]"
446
- onClick={onToggleExpand}
447
- >
448
- <img
449
- src={serviceItem?.icons?.downArrow}
450
- alt="down arrow"
451
- className={`transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`}
452
- style={{
453
- width: "14px",
454
- height: "8px",
455
- filter: "brightness(0) invert(1)",
456
- }}
457
- />
458
- </div>
459
- </div>
460
- </div>
461
- </div>
462
-
463
- {isItemExpanded && (
464
- <div
465
- className="flex flex-col gap-[10px]"
466
- style={{
467
- opacity: isItemExpanded ? 1 : 0,
468
- transition: "opacity 250ms ease-in-out",
469
- }}
470
- >
471
- <div
472
- className={` text-[12px] ${
473
- isItemExpanded ? "pt-[14px] pb-[6px]" : "py-0"
474
- }`}
475
- style={{ transition: "padding 300ms ease-in-out" }}
476
- >
477
- <span className="bold-text">¿Cómo funciona?</span>
478
-
479
- <div className="flex flex-col">
480
- {HOW_IT_WORKS_STEPS.map((step) => (
481
- <div className="flex items-start gap-[8px] mt-[10px]">
482
- <div>
483
- <FeatureStepIcon icon={step.icon} />
484
- </div>
485
- <div className="flex flex-col">
486
- <span className="bold-text">{step.name}</span>
487
- <span>{step.text}</span>
488
- </div>
489
- </div>
490
- ))}
491
- </div>
492
- </div>
493
- </div>
494
- )}
495
- </div>
496
-
497
- {/* TOP BADGE — "Remate | Termina en 09:55 min" hardcoded, no countdown hook */}
498
- <div className="absolute -top-[13px] left-0 w-full flex items-center justify-center gap-[12px] z-10 ">
499
- <div className="flex items-center gap-[6px] py-[5px] px-[14px] rounded-[38px] text-[12.5px] bg-[#FF8F45] text-white whitespace-nowrap">
500
- <span className="flex items-center">
501
- <div className="mb-[2px]">
502
- <LottiePlayer
503
- animationData={flameAnimation}
504
- width="16px"
505
- height="16px"
506
- />
507
- </div>
508
- <span className="bold-text text-[white] ml-[3px]">
509
- Remate&nbsp;
510
- </span>{" "}
511
- <span className="text-[white]">términa en</span>{" "}
512
- <span
513
- className="bold-text text-end"
514
- ref={(node) => commonService.startCountdown(node, 599)}
515
- style={{
516
- fontVariantNumeric: "tabular-nums",
517
- display: "inline-block",
518
- color: "#fff",
519
- minWidth: "40px",
520
- }}
521
- />
522
- </span>
523
- </div>
524
- </div>
525
- </div>
526
- );
527
- };
528
-
529
- export default FeatureServiceUiMobile;