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