kupos-ui-components-lib 10.4.5 → 10.4.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/styles.css +6 -9
- package/dist/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.js +138 -98
- package/dist/ui/FeatureServiceUI/FeatureServiceUi.js +33 -7
- package/package.json +1 -1
- package/src/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.tsx +267 -174
- package/src/ui/FeatureServiceUI/FeatureServiceUi.tsx +41 -7
package/dist/styles.css
CHANGED
|
@@ -189,9 +189,6 @@
|
|
|
189
189
|
.m-\[auto\] {
|
|
190
190
|
margin: auto;
|
|
191
191
|
}
|
|
192
|
-
.mx-\[6px\] {
|
|
193
|
-
margin-inline: 6px;
|
|
194
|
-
}
|
|
195
192
|
.mx-auto {
|
|
196
193
|
margin-inline: auto;
|
|
197
194
|
}
|
|
@@ -375,12 +372,12 @@
|
|
|
375
372
|
.h-\[12px\] {
|
|
376
373
|
height: 12px;
|
|
377
374
|
}
|
|
378
|
-
.h-\[13px\] {
|
|
379
|
-
height: 13px;
|
|
380
|
-
}
|
|
381
375
|
.h-\[14px\] {
|
|
382
376
|
height: 14px;
|
|
383
377
|
}
|
|
378
|
+
.h-\[15px\] {
|
|
379
|
+
height: 15px;
|
|
380
|
+
}
|
|
384
381
|
.h-\[16px\] {
|
|
385
382
|
height: 16px;
|
|
386
383
|
}
|
|
@@ -432,12 +429,12 @@
|
|
|
432
429
|
.w-\[12px\] {
|
|
433
430
|
width: 12px;
|
|
434
431
|
}
|
|
435
|
-
.w-\[13px\] {
|
|
436
|
-
width: 13px;
|
|
437
|
-
}
|
|
438
432
|
.w-\[14px\] {
|
|
439
433
|
width: 14px;
|
|
440
434
|
}
|
|
435
|
+
.w-\[15px\] {
|
|
436
|
+
width: 15px;
|
|
437
|
+
}
|
|
441
438
|
.w-\[16px\] {
|
|
442
439
|
width: 16px;
|
|
443
440
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import LottiePlayer from "../../assets/LottiePlayer";
|
|
3
3
|
import commonService from "../../utils/CommonService";
|
|
4
|
-
import flameAnimation from "../../assets/images/anims/service_list/flame_anim.json";
|
|
5
4
|
const HARDCODED_OPERATORS = [
|
|
6
5
|
{
|
|
7
6
|
logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
|
|
@@ -23,7 +22,7 @@ const HARDCODED_OPERATORS = [
|
|
|
23
22
|
},
|
|
24
23
|
];
|
|
25
24
|
const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity = 1, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData = undefined, }) => {
|
|
26
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
25
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
27
26
|
// Use wow_deal data if available, otherwise fall back to serviceItem operators or hardcoded
|
|
28
27
|
const operators = ((_a = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.services) === null || _a === void 0 ? void 0 : _a.length) > 0
|
|
29
28
|
? wowDealData.services.slice(0, 3).map((service) => ({
|
|
@@ -92,8 +91,32 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
92
91
|
selectedSlotService.original_price) *
|
|
93
92
|
100)
|
|
94
93
|
: savingsPercent;
|
|
94
|
+
// Convert "HH:MM" (24h) → "H:MM AM/PM"
|
|
95
|
+
const formatTimeAmPm = (time) => {
|
|
96
|
+
const match = time.match(/(\d{1,2}):(\d{2})/);
|
|
97
|
+
if (!match)
|
|
98
|
+
return time;
|
|
99
|
+
let h = parseInt(match[1], 10);
|
|
100
|
+
const m = match[2];
|
|
101
|
+
const period = h >= 12 ? "PM" : "AM";
|
|
102
|
+
h = h % 12 || 12;
|
|
103
|
+
return `${h}:${m} ${period}`;
|
|
104
|
+
};
|
|
105
|
+
// Convert all HH:MM occurrences in a label string to AM/PM
|
|
106
|
+
const formatLabelAmPm = (label) => label.replace(/(\d{1,2}):(\d{2})/g, (_, hh, mm) => {
|
|
107
|
+
let h = parseInt(hh, 10);
|
|
108
|
+
const period = h >= 12 ? "PM" : "AM";
|
|
109
|
+
h = h % 12 || 12;
|
|
110
|
+
return `${h}:${mm} ${period}`;
|
|
111
|
+
});
|
|
95
112
|
// The label shown on the dropdown button
|
|
96
|
-
const departureRange = (activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label) || `Entre ${dealWindowFrom} y ${dealWindowTo}
|
|
113
|
+
const departureRange = formatLabelAmPm((activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label) || `Entre ${dealWindowFrom} y ${dealWindowTo}`);
|
|
114
|
+
const currentWindowFrom = (activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label)
|
|
115
|
+
? activeSlot.label.split(" - ")[0]
|
|
116
|
+
: dealWindowFrom;
|
|
117
|
+
const currentWindowTo = (activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label)
|
|
118
|
+
? activeSlot.label.split(" - ")[1]
|
|
119
|
+
: dealWindowTo;
|
|
97
120
|
const isItemExpanded = serviceItem.id === isFeatureDropDownExpand ||
|
|
98
121
|
isFeatureDropDownExpand === true;
|
|
99
122
|
const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
|
|
@@ -104,7 +127,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
104
127
|
{
|
|
105
128
|
icon: "flexible",
|
|
106
129
|
name: "Salida flexible",
|
|
107
|
-
text: `Viajas en un horario entre las ${
|
|
130
|
+
text: `Viajas en un horario entre las ${currentWindowFrom} y las ${currentWindowTo} del día elegido.`,
|
|
108
131
|
},
|
|
109
132
|
{
|
|
110
133
|
icon: "bus",
|
|
@@ -159,7 +182,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
159
182
|
React.createElement("div", { className: "bold-text font-[9px]", style: {
|
|
160
183
|
backgroundColor: "#FF5C60",
|
|
161
184
|
padding: "4px 8px",
|
|
162
|
-
borderRadius: "
|
|
185
|
+
borderRadius: "6px",
|
|
163
186
|
color: "#fff",
|
|
164
187
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
165
188
|
whiteSpace: "nowrap",
|
|
@@ -172,68 +195,77 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
172
195
|
React.createElement("div", { id: `service-card-${serviceItem.id}`, className: "bg-[#0C1421] text-white mx-auto relative rounded-[14px] p-[14px] text-[13.33px]" },
|
|
173
196
|
React.createElement("div", { className: "flex flex-col gap-[10px]" },
|
|
174
197
|
React.createElement("div", { className: " text-[white]" },
|
|
175
|
-
React.createElement("div", { className: "flex flex-col gap-[
|
|
176
|
-
React.createElement("div", { className: "flex items-
|
|
177
|
-
React.createElement("
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
React.createElement("
|
|
189
|
-
React.createElement("span", null,
|
|
190
|
-
React.createElement("
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
198
|
+
React.createElement("div", { className: "flex flex-col gap-[6px] relative" },
|
|
199
|
+
React.createElement("div", { className: "flex items-start gap-[10px]" },
|
|
200
|
+
React.createElement("div", { className: "flex flex-col items-center shrink-0", style: { paddingTop: "2px" } },
|
|
201
|
+
React.createElement("img", { src: (_d = serviceItem.icons) === null || _d === void 0 ? void 0 : _d.whiteOrigin, alt: "origin", className: `w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}` }),
|
|
202
|
+
React.createElement("div", { style: {
|
|
203
|
+
width: "1.5px",
|
|
204
|
+
flex: 1,
|
|
205
|
+
minHeight: "8px",
|
|
206
|
+
backgroundColor: "rgba(255,255,255,0.35)",
|
|
207
|
+
margin: "2px 0",
|
|
208
|
+
} }),
|
|
209
|
+
React.createElement("img", { src: (_e = serviceItem.icons) === null || _e === void 0 ? void 0 : _e.whiteDestination, alt: "destination", className: `w-[15px] h-[15px] shrink-0 ${isSoldOut ? "grayscale" : ""}`, style: { opacity: isSoldOut ? 0.5 : 1 } })),
|
|
210
|
+
React.createElement("div", { className: "flex flex-col gap-[6px]" },
|
|
211
|
+
React.createElement("div", { className: "flex items-center gap-[6px]" },
|
|
212
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, cityOrigin === null || cityOrigin === void 0 ? void 0 : cityOrigin.label.split(",")[0]),
|
|
213
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, "\u2192"),
|
|
214
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, cityDestination === null || cityDestination === void 0 ? void 0 : cityDestination.label.split(",")[0])),
|
|
215
|
+
React.createElement("div", { className: "kupos-time-dd relative", tabIndex: 0, onBlur: (e) => {
|
|
216
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
217
|
+
onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
|
|
218
|
+
}
|
|
219
|
+
}, style: { outline: "none", marginTop: "3px" } },
|
|
220
|
+
React.createElement("button", { type: "button", onClick: () => onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(isThisTimeDropdownOpen ? null : serviceItem.id), className: "flex cursor-pointer select-none items-center gap-[6px] border-none bg-transparent p-0 bold-text text-[13px] text-[white]" },
|
|
221
|
+
React.createElement("span", null, departureRange),
|
|
222
|
+
React.createElement("img", { src: (_f = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _f === void 0 ? void 0 : _f.downArrow, alt: "down arrow", className: `kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`, style: {
|
|
223
|
+
width: "12px",
|
|
224
|
+
height: "8px",
|
|
225
|
+
filter: "brightness(0) invert(1)",
|
|
226
|
+
} })),
|
|
227
|
+
isThisTimeDropdownOpen && (React.createElement(React.Fragment, null,
|
|
228
|
+
React.createElement("div", { className: "absolute left-0 top-[calc(100%+10px)]", style: {
|
|
229
|
+
zIndex: 20,
|
|
230
|
+
backgroundColor: "#fff",
|
|
231
|
+
borderRadius: "14px",
|
|
232
|
+
minWidth: "190px",
|
|
233
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
|
|
234
|
+
overflow: "hidden",
|
|
235
|
+
padding: "6px 0",
|
|
236
|
+
} }, availableTimeSlots.map((slot) => {
|
|
237
|
+
const isActive = slot.label === selectedTimeSlot ||
|
|
238
|
+
slot === activeSlot;
|
|
239
|
+
// Count services in this slot
|
|
240
|
+
const count = services.filter((s) => {
|
|
241
|
+
const depMin = commonService.timeToMinutes(s.departure_time);
|
|
242
|
+
return (depMin >= slot.start && depMin < slot.end);
|
|
243
|
+
}).length;
|
|
244
|
+
return (React.createElement("button", { key: slot.label, type: "button", onClick: () => {
|
|
245
|
+
onTimeSlotChange === null || onTimeSlotChange === void 0 ? void 0 : onTimeSlotChange(slot.label);
|
|
246
|
+
onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
|
|
247
|
+
// const slotServices = services.filter((s) => {
|
|
248
|
+
// const depMin = commonService.timeToMinutes(
|
|
249
|
+
// s.departure_time,
|
|
250
|
+
// );
|
|
251
|
+
// return (
|
|
252
|
+
// depMin >= slot.start && depMin < slot.end
|
|
253
|
+
// );
|
|
254
|
+
// });
|
|
255
|
+
// console.log(
|
|
256
|
+
// "Selected slot label:",
|
|
257
|
+
// slot.label,
|
|
258
|
+
// );
|
|
259
|
+
// console.log(
|
|
260
|
+
// "Services in selected slot:",
|
|
261
|
+
// slotServices,
|
|
262
|
+
// );
|
|
263
|
+
}, className: `flex w-full cursor-pointer items-center justify-between 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]"}` },
|
|
264
|
+
React.createElement("span", null, slot.label),
|
|
265
|
+
count > 0 && (React.createElement("span", { className: `text-[11px] rounded-full px-[6px] py-[2px] font-bold ${isActive
|
|
266
|
+
? "bg-[white] text-[#FF5C60]"
|
|
267
|
+
: "bg-[#FF5C60] text-[white]"}` }, count))));
|
|
268
|
+
}))))))))),
|
|
237
269
|
React.createElement("div", { className: "border-t border-[#363c48] my-[8px]" }),
|
|
238
270
|
React.createElement("div", null,
|
|
239
271
|
React.createElement("span", { className: "block w-full text-[14px] bold-text text-[white] mb-[10px]", style: { textAlign: "center" } },
|
|
@@ -244,29 +276,38 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
244
276
|
"operadores compitiendo ",
|
|
245
277
|
React.createElement("br", null),
|
|
246
278
|
"por tu compra"),
|
|
247
|
-
React.createElement("div", { className: "flex gap-[8px] text-[white]", style: { width: "100%" } }, (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
279
|
+
React.createElement("div", { className: "flex gap-[8px] text-[white]", style: { width: "100%", justifyContent: "center" } }, (() => {
|
|
280
|
+
const displayOps = servicesInActiveSlot.length > 0
|
|
281
|
+
? servicesInActiveSlot.slice(0, 3).map((s) => ({
|
|
282
|
+
logo: s.operator_logo_url,
|
|
283
|
+
name: s.operator_name,
|
|
284
|
+
time: s.departure_time,
|
|
285
|
+
seatsAvailable: `${s.available_seats} disponibles`,
|
|
286
|
+
}))
|
|
287
|
+
: operators;
|
|
288
|
+
const isSingle = displayOps.length === 1;
|
|
289
|
+
return displayOps.map((op, idx) => (React.createElement("div", { key: idx, className: "flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]", style: {
|
|
290
|
+
flex: isSingle ? "none" : 1,
|
|
291
|
+
width: isSingle ? "32%" : undefined,
|
|
292
|
+
margin: isSingle ? "0 auto" : undefined,
|
|
293
|
+
minWidth: 0,
|
|
294
|
+
height: "100px",
|
|
295
|
+
border: "1px solid #363c48",
|
|
296
|
+
backgroundColor: "#1a202e",
|
|
297
|
+
padding: "14px 6px",
|
|
298
|
+
} },
|
|
299
|
+
React.createElement("img", { src: op.logo, alt: op.name, onError: (e) => {
|
|
300
|
+
var _a;
|
|
301
|
+
e.currentTarget.src =
|
|
302
|
+
((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_details) === null || _a === void 0 ? void 0 : _a[0]) ||
|
|
303
|
+
"/images/service-list/bus-icon.svg";
|
|
304
|
+
}, className: `object-contain ${isSoldOut ? "grayscale" : ""}`, style: {
|
|
305
|
+
height: "24px",
|
|
306
|
+
maxWidth: "100%",
|
|
307
|
+
} }),
|
|
308
|
+
React.createElement("span", { className: "text-[12px] truncate max-w-full text-center" }, op.name),
|
|
309
|
+
React.createElement("span", { className: "text-[11px] whitespace-nowrap" }, op === null || op === void 0 ? void 0 : op.seatsAvailable))));
|
|
310
|
+
})()),
|
|
270
311
|
React.createElement("div", { className: "flex w-full items-center justify-center gap-[6px] text-[12px] mt-[12px]", style: {
|
|
271
312
|
padding: "4px 14px",
|
|
272
313
|
} },
|
|
@@ -375,19 +416,18 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
375
416
|
React.createElement("span", { className: "bold-text" }, step.name),
|
|
376
417
|
React.createElement("span", null, step.text)))))))))),
|
|
377
418
|
React.createElement("div", { className: "absolute -top-[13px] left-0 w-full flex items-center justify-center gap-[12px] z-10 " },
|
|
378
|
-
React.createElement("div", { className: "flex items-center gap-[6px] py-[5px] px-[14px] rounded-[38px] text-[12.5px]
|
|
419
|
+
React.createElement("div", { className: "flex items-center gap-[6px] py-[5px] px-[14px] rounded-[38px] text-[12.5px] text-white whitespace-nowrap", style: { backgroundColor: "rgba(255, 222, 223, 0.93)" } },
|
|
379
420
|
React.createElement("span", { className: "flex items-center" },
|
|
380
|
-
React.createElement("
|
|
381
|
-
|
|
382
|
-
React.createElement("span", { className: "bold-text text-[white] ml-[3px]" }, "Remate\u00A0"),
|
|
421
|
+
((_h = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _h === void 0 ? void 0 : _h.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[12px] w-[12px] object-contain mr-[5px]", style: { filter: isSoldOut ? "grayscale" : "" } })) : null,
|
|
422
|
+
React.createElement("span", { className: "bold-text text-[#464647] ml-[3px]" }, "Remate\u00A0"),
|
|
383
423
|
" ",
|
|
384
|
-
React.createElement("span", { className: "text-[
|
|
424
|
+
React.createElement("span", { className: "text-[#464647]" }, "t\u00E9rmina en\u00A0"),
|
|
385
425
|
" ",
|
|
386
|
-
React.createElement("span", { className: "bold-text text-end", ref: (node) => commonService.
|
|
426
|
+
React.createElement("span", { className: "bold-text text-end", ref: (node) => commonService.startDealCountdown(node, getCountdownSeconds()), style: {
|
|
387
427
|
fontVariantNumeric: "tabular-nums",
|
|
388
428
|
display: "inline-block",
|
|
389
|
-
color: "#
|
|
390
|
-
minWidth: "
|
|
429
|
+
color: "#FF5C60",
|
|
430
|
+
minWidth: "50px",
|
|
391
431
|
} }))))));
|
|
392
432
|
};
|
|
393
433
|
export default FeatureServiceUiMobile;
|
|
@@ -31,6 +31,7 @@ const HARDCODED_OPERATORS = [
|
|
|
31
31
|
seatsAvailable: "3 disponibles",
|
|
32
32
|
},
|
|
33
33
|
];
|
|
34
|
+
const expandedStateMap = {};
|
|
34
35
|
const FeatureServiceUi = ({ serviceItem, showTopLabel, getAnimationIcon, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity = 1, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData = undefined, }) => {
|
|
35
36
|
var _a, _b, _c, _d, _e;
|
|
36
37
|
console.log("🚀 ~ FeatureServiceUi ~ selectedTimeSlot:", selectedTimeSlot);
|
|
@@ -112,8 +113,10 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, getAnimationIcon, cityOri
|
|
|
112
113
|
const currentWindowTo = (activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label)
|
|
113
114
|
? activeSlot.label.split(" - ")[1]
|
|
114
115
|
: dealWindowTo;
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
if (expandedStateMap[serviceItem.id] === undefined) {
|
|
117
|
+
expandedStateMap[serviceItem.id] = true;
|
|
118
|
+
}
|
|
119
|
+
const isItemExpanded = expandedStateMap[serviceItem.id];
|
|
117
120
|
const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
|
|
118
121
|
const canDecreaseTicketQuantity = ticketQuantity > 1;
|
|
119
122
|
const availableSeats = (_c = selectedSlotService === null || selectedSlotService === void 0 ? void 0 : selectedSlotService.available_seats) !== null && _c !== void 0 ? _c : maxSeatsPerBooking;
|
|
@@ -403,18 +406,41 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, getAnimationIcon, cityOri
|
|
|
403
406
|
animation: "feature-spin 0.7s linear infinite",
|
|
404
407
|
flexShrink: 0,
|
|
405
408
|
} })))),
|
|
406
|
-
React.createElement("div", { className: `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`, onClick:
|
|
409
|
+
React.createElement("div", { id: `feature-arrow-${serviceItem.id}`, className: `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`, onClick: (e) => {
|
|
410
|
+
expandedStateMap[serviceItem.id] =
|
|
411
|
+
!expandedStateMap[serviceItem.id];
|
|
412
|
+
if (onToggleExpand)
|
|
413
|
+
onToggleExpand(e);
|
|
414
|
+
const contentNode = document.getElementById(`feature-content-${serviceItem.id}`);
|
|
415
|
+
const innerNode = document.getElementById(`feature-inner-content-${serviceItem.id}`);
|
|
416
|
+
const arrowNode = document.getElementById(`feature-arrow-${serviceItem.id}`);
|
|
417
|
+
if (contentNode && innerNode && arrowNode) {
|
|
418
|
+
const isExpanded = expandedStateMap[serviceItem.id];
|
|
419
|
+
if (isExpanded) {
|
|
420
|
+
contentNode.style.gridTemplateRows = "1fr";
|
|
421
|
+
contentNode.style.opacity = "1";
|
|
422
|
+
innerNode.className = `min-h-0 overflow-hidden px-[16px] text-[13.33px] pt-[14px] pb-[6px]`;
|
|
423
|
+
arrowNode.className = `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out rotate-180`;
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
contentNode.style.gridTemplateRows = "0fr";
|
|
427
|
+
contentNode.style.opacity = "0";
|
|
428
|
+
innerNode.className = `min-h-0 overflow-hidden px-[16px] text-[13.33px] pt-0 pb-0`;
|
|
429
|
+
arrowNode.className = `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out `;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
} },
|
|
407
433
|
React.createElement("img", { src: (_e = serviceItem.icons) === null || _e === void 0 ? void 0 : _e.downArrow, alt: "down arrow", style: {
|
|
408
434
|
width: "14px",
|
|
409
435
|
height: "8px",
|
|
410
436
|
filter: "brightness(0) invert(1)",
|
|
411
437
|
} })))),
|
|
412
|
-
React.createElement("div", { className: "grid", style: {
|
|
413
|
-
gridTemplateRows: "1fr",
|
|
414
|
-
opacity: 1,
|
|
438
|
+
React.createElement("div", { id: `feature-content-${serviceItem.id}`, className: "grid", style: {
|
|
439
|
+
gridTemplateRows: isItemExpanded ? "1fr" : "0fr",
|
|
440
|
+
opacity: isItemExpanded ? 1 : 0,
|
|
415
441
|
transition: "grid-template-rows 300ms ease-in-out, opacity 250ms ease-in-out",
|
|
416
442
|
} },
|
|
417
|
-
React.createElement("div", { className: `min-h-0 overflow-hidden px-[16px] text-[13.33px] pt-[14px] pb-[6px]`, style: { transition: "padding 300ms ease-in-out" } },
|
|
443
|
+
React.createElement("div", { id: `feature-inner-content-${serviceItem.id}`, className: `min-h-0 overflow-hidden px-[16px] text-[13.33px] ${isItemExpanded ? "pt-[14px] pb-[6px]" : "pt-0 pb-0"}`, style: { transition: "padding 300ms ease-in-out" } },
|
|
418
444
|
React.createElement("span", { className: "bold-text" }, "\u00BFC\u00F3mo funciona?"),
|
|
419
445
|
React.createElement("div", { className: "mt-[14px] grid grid-cols-4 gap-[20px] px-[16px] " }, HOW_IT_WORKS_STEPS.map((step) => (React.createElement("div", { key: step.name, className: "flex flex-col items-center text-center text-[#272727]" },
|
|
420
446
|
React.createElement(FeatureStepIcon, { icon: step.icon }),
|
package/package.json
CHANGED
|
@@ -131,9 +131,37 @@ const FeatureServiceUiMobile = ({
|
|
|
131
131
|
)
|
|
132
132
|
: savingsPercent;
|
|
133
133
|
|
|
134
|
+
// Convert "HH:MM" (24h) → "H:MM AM/PM"
|
|
135
|
+
const formatTimeAmPm = (time: string): string => {
|
|
136
|
+
const match = time.match(/(\d{1,2}):(\d{2})/);
|
|
137
|
+
if (!match) return time;
|
|
138
|
+
let h = parseInt(match[1], 10);
|
|
139
|
+
const m = match[2];
|
|
140
|
+
const period = h >= 12 ? "PM" : "AM";
|
|
141
|
+
h = h % 12 || 12;
|
|
142
|
+
return `${h}:${m} ${period}`;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Convert all HH:MM occurrences in a label string to AM/PM
|
|
146
|
+
const formatLabelAmPm = (label: string): string =>
|
|
147
|
+
label.replace(/(\d{1,2}):(\d{2})/g, (_, hh, mm) => {
|
|
148
|
+
let h = parseInt(hh, 10);
|
|
149
|
+
const period = h >= 12 ? "PM" : "AM";
|
|
150
|
+
h = h % 12 || 12;
|
|
151
|
+
return `${h}:${mm} ${period}`;
|
|
152
|
+
});
|
|
153
|
+
|
|
134
154
|
// The label shown on the dropdown button
|
|
135
|
-
const departureRange =
|
|
136
|
-
activeSlot?.label || `Entre ${dealWindowFrom} y ${dealWindowTo}
|
|
155
|
+
const departureRange = formatLabelAmPm(
|
|
156
|
+
activeSlot?.label || `Entre ${dealWindowFrom} y ${dealWindowTo}`,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const currentWindowFrom = activeSlot?.label
|
|
160
|
+
? activeSlot.label.split(" - ")[0]
|
|
161
|
+
: dealWindowFrom;
|
|
162
|
+
const currentWindowTo = activeSlot?.label
|
|
163
|
+
? activeSlot.label.split(" - ")[1]
|
|
164
|
+
: dealWindowTo;
|
|
137
165
|
|
|
138
166
|
const isItemExpanded =
|
|
139
167
|
serviceItem.id === isFeatureDropDownExpand ||
|
|
@@ -149,7 +177,7 @@ const FeatureServiceUiMobile = ({
|
|
|
149
177
|
{
|
|
150
178
|
icon: "flexible",
|
|
151
179
|
name: "Salida flexible",
|
|
152
|
-
text: `Viajas en un horario entre las ${
|
|
180
|
+
text: `Viajas en un horario entre las ${currentWindowFrom} y las ${currentWindowTo} del día elegido.`,
|
|
153
181
|
},
|
|
154
182
|
{
|
|
155
183
|
icon: "bus",
|
|
@@ -213,7 +241,7 @@ const FeatureServiceUiMobile = ({
|
|
|
213
241
|
style={{
|
|
214
242
|
backgroundColor: "#FF5C60",
|
|
215
243
|
padding: "4px 8px",
|
|
216
|
-
borderRadius: "
|
|
244
|
+
borderRadius: "6px",
|
|
217
245
|
color: "#fff",
|
|
218
246
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
219
247
|
whiteSpace: "nowrap",
|
|
@@ -230,141 +258,160 @@ const FeatureServiceUiMobile = ({
|
|
|
230
258
|
>
|
|
231
259
|
<div className="flex flex-col gap-[10px]">
|
|
232
260
|
<div className=" text-[white]">
|
|
233
|
-
<div className="flex flex-col gap-[
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
alt="origin"
|
|
238
|
-
className={`w-[13px] h-[13px] shrink-0 ${
|
|
239
|
-
isSoldOut ? "grayscale" : ""
|
|
240
|
-
}`}
|
|
241
|
-
/>
|
|
242
|
-
|
|
243
|
-
<span className="text-[14px] bold-text">
|
|
244
|
-
{cityOrigin?.label.split(",")[0]}
|
|
245
|
-
</span>
|
|
246
|
-
|
|
247
|
-
<span className="mx-[6px] text-[14px] bold-text">→</span>
|
|
248
|
-
|
|
249
|
-
<img
|
|
250
|
-
src={serviceItem.icons?.whiteDestination}
|
|
251
|
-
alt="destination"
|
|
252
|
-
className={`w-[13px] h-[13px] shrink-0 ${
|
|
253
|
-
isSoldOut ? "grayscale" : ""
|
|
254
|
-
}`}
|
|
255
|
-
style={{ opacity: isSoldOut ? 0.5 : 1 }}
|
|
256
|
-
/>
|
|
257
|
-
|
|
258
|
-
<span className="text-[14px] bold-text">
|
|
259
|
-
{cityDestination?.label.split(",")[0]}
|
|
260
|
-
</span>
|
|
261
|
-
</div>
|
|
262
|
-
|
|
263
|
-
<div className="flex items-center gap-[6px]">
|
|
261
|
+
<div className="flex flex-col gap-[6px] relative">
|
|
262
|
+
{/* Origin / Destination row with connecting-line icons */}
|
|
263
|
+
<div className="flex items-start gap-[10px]">
|
|
264
|
+
{/* Icon column with connecting line */}
|
|
264
265
|
<div
|
|
265
|
-
className="
|
|
266
|
-
|
|
267
|
-
onBlur={(e) => {
|
|
268
|
-
if (!e.currentTarget.contains(e.relatedTarget as Node)) {
|
|
269
|
-
onTimeDropdownToggle?.(null);
|
|
270
|
-
}
|
|
271
|
-
}}
|
|
272
|
-
style={{ outline: "none" }}
|
|
266
|
+
className="flex flex-col items-center shrink-0"
|
|
267
|
+
style={{ paddingTop: "2px" }}
|
|
273
268
|
>
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
269
|
+
<img
|
|
270
|
+
src={serviceItem.icons?.whiteOrigin}
|
|
271
|
+
alt="origin"
|
|
272
|
+
className={`w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}`}
|
|
273
|
+
/>
|
|
274
|
+
<div
|
|
275
|
+
style={{
|
|
276
|
+
width: "1.5px",
|
|
277
|
+
flex: 1,
|
|
278
|
+
minHeight: "8px",
|
|
279
|
+
backgroundColor: "rgba(255,255,255,0.35)",
|
|
280
|
+
margin: "2px 0",
|
|
281
|
+
}}
|
|
282
|
+
/>
|
|
283
|
+
<img
|
|
284
|
+
src={serviceItem.icons?.whiteDestination}
|
|
285
|
+
alt="destination"
|
|
286
|
+
className={`w-[15px] h-[15px] shrink-0 ${isSoldOut ? "grayscale" : ""}`}
|
|
287
|
+
style={{ opacity: isSoldOut ? 0.5 : 1 }}
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
{/* Text column */}
|
|
291
|
+
<div className="flex flex-col gap-[6px]">
|
|
292
|
+
<div className="flex items-center gap-[6px]">
|
|
293
|
+
<span className="text-[14px] bold-text">
|
|
294
|
+
{cityOrigin?.label.split(",")[0]}
|
|
295
|
+
</span>
|
|
296
|
+
<span className="text-[14px] bold-text">→</span>
|
|
297
|
+
<span className="text-[14px] bold-text">
|
|
298
|
+
{cityDestination?.label.split(",")[0]}
|
|
299
|
+
</span>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<div
|
|
303
|
+
className="kupos-time-dd relative"
|
|
304
|
+
tabIndex={0}
|
|
305
|
+
onBlur={(e) => {
|
|
306
|
+
if (
|
|
307
|
+
!e.currentTarget.contains(e.relatedTarget as Node)
|
|
308
|
+
) {
|
|
309
|
+
onTimeDropdownToggle?.(null);
|
|
310
|
+
}
|
|
311
|
+
}}
|
|
312
|
+
style={{ outline: "none", marginTop: "3px" }}
|
|
282
313
|
>
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
<div
|
|
298
|
-
className="absolute left-0 top-[calc(100%+10px)]"
|
|
314
|
+
<button
|
|
315
|
+
type="button"
|
|
316
|
+
onClick={() =>
|
|
317
|
+
onTimeDropdownToggle?.(
|
|
318
|
+
isThisTimeDropdownOpen ? null : serviceItem.id,
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
className="flex cursor-pointer select-none items-center gap-[6px] border-none bg-transparent p-0 bold-text text-[13px] text-[white]"
|
|
322
|
+
>
|
|
323
|
+
<span>{departureRange}</span>
|
|
324
|
+
<img
|
|
325
|
+
src={serviceItem?.icons?.downArrow}
|
|
326
|
+
alt="down arrow"
|
|
327
|
+
className={`kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`}
|
|
299
328
|
style={{
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
minWidth: "190px",
|
|
304
|
-
boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
|
|
305
|
-
overflow: "hidden",
|
|
306
|
-
padding: "6px 0",
|
|
329
|
+
width: "12px",
|
|
330
|
+
height: "8px",
|
|
331
|
+
filter: "brightness(0) invert(1)",
|
|
307
332
|
}}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
333
|
+
/>
|
|
334
|
+
</button>
|
|
335
|
+
{isThisTimeDropdownOpen && (
|
|
336
|
+
<>
|
|
337
|
+
<div
|
|
338
|
+
className="absolute left-0 top-[calc(100%+10px)]"
|
|
339
|
+
style={{
|
|
340
|
+
zIndex: 20,
|
|
341
|
+
backgroundColor: "#fff",
|
|
342
|
+
borderRadius: "14px",
|
|
343
|
+
minWidth: "190px",
|
|
344
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
|
|
345
|
+
overflow: "hidden",
|
|
346
|
+
padding: "6px 0",
|
|
347
|
+
}}
|
|
348
|
+
>
|
|
349
|
+
{availableTimeSlots.map((slot) => {
|
|
350
|
+
const isActive =
|
|
351
|
+
slot.label === selectedTimeSlot ||
|
|
352
|
+
slot === activeSlot;
|
|
353
|
+
// Count services in this slot
|
|
354
|
+
const count = services.filter((s) => {
|
|
355
|
+
const depMin = commonService.timeToMinutes(
|
|
356
|
+
s.departure_time,
|
|
357
|
+
);
|
|
358
|
+
return (
|
|
359
|
+
depMin >= slot.start && depMin < slot.end
|
|
360
|
+
);
|
|
361
|
+
}).length;
|
|
362
|
+
return (
|
|
363
|
+
<button
|
|
364
|
+
key={slot.label}
|
|
365
|
+
type="button"
|
|
366
|
+
onClick={() => {
|
|
367
|
+
onTimeSlotChange?.(slot.label);
|
|
368
|
+
onTimeDropdownToggle?.(null);
|
|
369
|
+
// const slotServices = services.filter((s) => {
|
|
370
|
+
// const depMin = commonService.timeToMinutes(
|
|
371
|
+
// s.departure_time,
|
|
372
|
+
// );
|
|
373
|
+
// return (
|
|
374
|
+
// depMin >= slot.start && depMin < slot.end
|
|
375
|
+
// );
|
|
376
|
+
// });
|
|
377
|
+
// console.log(
|
|
378
|
+
// "Selected slot label:",
|
|
379
|
+
// slot.label,
|
|
380
|
+
// );
|
|
381
|
+
// console.log(
|
|
382
|
+
// "Services in selected slot:",
|
|
383
|
+
// slotServices,
|
|
384
|
+
// );
|
|
385
|
+
}}
|
|
386
|
+
className={`flex w-full cursor-pointer items-center justify-between 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]"}`}
|
|
387
|
+
>
|
|
388
|
+
<span>{slot.label}</span>
|
|
389
|
+
{count > 0 && (
|
|
390
|
+
<span
|
|
391
|
+
className={`text-[11px] rounded-full px-[6px] py-[2px] font-bold ${
|
|
392
|
+
isActive
|
|
393
|
+
? "bg-[white] text-[#FF5C60]"
|
|
394
|
+
: "bg-[#FF5C60] text-[white]"
|
|
395
|
+
}`}
|
|
396
|
+
>
|
|
397
|
+
{count}
|
|
398
|
+
</span>
|
|
399
|
+
)}
|
|
400
|
+
</button>
|
|
317
401
|
);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
type="button"
|
|
324
|
-
onClick={() => {
|
|
325
|
-
onTimeSlotChange?.(slot.label);
|
|
326
|
-
onTimeDropdownToggle?.(null);
|
|
327
|
-
// const slotServices = services.filter((s) => {
|
|
328
|
-
// const depMin = commonService.timeToMinutes(
|
|
329
|
-
// s.departure_time,
|
|
330
|
-
// );
|
|
331
|
-
// return (
|
|
332
|
-
// depMin >= slot.start && depMin < slot.end
|
|
333
|
-
// );
|
|
334
|
-
// });
|
|
335
|
-
// console.log(
|
|
336
|
-
// "Selected slot label:",
|
|
337
|
-
// slot.label,
|
|
338
|
-
// );
|
|
339
|
-
// console.log(
|
|
340
|
-
// "Services in selected slot:",
|
|
341
|
-
// slotServices,
|
|
342
|
-
// );
|
|
343
|
-
}}
|
|
344
|
-
className={`flex w-full cursor-pointer items-center justify-between 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]"}`}
|
|
345
|
-
>
|
|
346
|
-
<span>{slot.label}</span>
|
|
347
|
-
{count > 0 && (
|
|
348
|
-
<span
|
|
349
|
-
className={`text-[11px] rounded-full px-[6px] py-[2px] font-bold ${
|
|
350
|
-
isActive
|
|
351
|
-
? "bg-[white] text-[#FF5C60]"
|
|
352
|
-
: "bg-[#FF5C60] text-[white]"
|
|
353
|
-
}`}
|
|
354
|
-
>
|
|
355
|
-
{count}
|
|
356
|
-
</span>
|
|
357
|
-
)}
|
|
358
|
-
</button>
|
|
359
|
-
);
|
|
360
|
-
})}
|
|
361
|
-
</div>
|
|
362
|
-
</>
|
|
363
|
-
)}
|
|
402
|
+
})}
|
|
403
|
+
</div>
|
|
404
|
+
</>
|
|
405
|
+
)}
|
|
406
|
+
</div>
|
|
364
407
|
</div>
|
|
408
|
+
{/* end text column */}
|
|
365
409
|
</div>
|
|
410
|
+
{/* end flex items-start row */}
|
|
366
411
|
</div>
|
|
412
|
+
{/* end flex-col gap-6 relative */}
|
|
367
413
|
</div>
|
|
414
|
+
{/* end text-[white] */}
|
|
368
415
|
|
|
369
416
|
<div className="border-t border-[#363c48] my-[8px]" />
|
|
370
417
|
|
|
@@ -379,54 +426,62 @@ const FeatureServiceUiMobile = ({
|
|
|
379
426
|
operadores compitiendo <br />
|
|
380
427
|
por tu compra
|
|
381
428
|
</span>
|
|
429
|
+
{/* Operator boxes: single full-width box when 1 operator, else flex row */}
|
|
382
430
|
<div
|
|
383
431
|
className="flex gap-[8px] text-[white]"
|
|
384
|
-
style={{ width: "100%" }}
|
|
432
|
+
style={{ width: "100%", justifyContent: "center" }}
|
|
385
433
|
>
|
|
386
|
-
{(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
onError={(e) => {
|
|
411
|
-
e.currentTarget.src =
|
|
412
|
-
serviceItem?.operator_details?.[0] ||
|
|
413
|
-
"/images/service-list/bus-icon.svg";
|
|
434
|
+
{(() => {
|
|
435
|
+
const displayOps =
|
|
436
|
+
servicesInActiveSlot.length > 0
|
|
437
|
+
? servicesInActiveSlot.slice(0, 3).map((s: any) => ({
|
|
438
|
+
logo: s.operator_logo_url,
|
|
439
|
+
name: s.operator_name,
|
|
440
|
+
time: s.departure_time,
|
|
441
|
+
seatsAvailable: `${s.available_seats} disponibles`,
|
|
442
|
+
}))
|
|
443
|
+
: operators;
|
|
444
|
+
const isSingle = displayOps.length === 1;
|
|
445
|
+
return displayOps.map((op, idx) => (
|
|
446
|
+
<div
|
|
447
|
+
key={idx}
|
|
448
|
+
className="flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]"
|
|
449
|
+
style={{
|
|
450
|
+
flex: isSingle ? "none" : 1,
|
|
451
|
+
width: isSingle ? "32%" : undefined,
|
|
452
|
+
margin: isSingle ? "0 auto" : undefined,
|
|
453
|
+
minWidth: 0,
|
|
454
|
+
height: "100px",
|
|
455
|
+
border: "1px solid #363c48",
|
|
456
|
+
backgroundColor: "#1a202e",
|
|
457
|
+
padding: "14px 6px",
|
|
414
458
|
}}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
459
|
+
>
|
|
460
|
+
<img
|
|
461
|
+
src={op.logo}
|
|
462
|
+
alt={op.name}
|
|
463
|
+
onError={(e) => {
|
|
464
|
+
e.currentTarget.src =
|
|
465
|
+
serviceItem?.operator_details?.[0] ||
|
|
466
|
+
"/images/service-list/bus-icon.svg";
|
|
467
|
+
}}
|
|
468
|
+
className={`object-contain ${
|
|
469
|
+
isSoldOut ? "grayscale" : ""
|
|
470
|
+
}`}
|
|
471
|
+
style={{
|
|
472
|
+
height: "24px",
|
|
473
|
+
maxWidth: "100%",
|
|
474
|
+
}}
|
|
475
|
+
/>
|
|
476
|
+
<span className="text-[12px] truncate max-w-full text-center">
|
|
477
|
+
{op.name}
|
|
478
|
+
</span>
|
|
479
|
+
<span className="text-[11px] whitespace-nowrap">
|
|
480
|
+
{op?.seatsAvailable}
|
|
481
|
+
</span>
|
|
482
|
+
</div>
|
|
483
|
+
));
|
|
484
|
+
})()}
|
|
430
485
|
</div>
|
|
431
486
|
|
|
432
487
|
<div
|
|
@@ -661,7 +716,7 @@ const FeatureServiceUiMobile = ({
|
|
|
661
716
|
|
|
662
717
|
{/* TOP BADGE — "Remate | Termina en 09:55 min" hardcoded, no countdown hook */}
|
|
663
718
|
<div className="absolute -top-[13px] left-0 w-full flex items-center justify-center gap-[12px] z-10 ">
|
|
664
|
-
<div className="flex items-center gap-[6px] py-[5px] px-[14px] rounded-[38px] text-[12.5px] bg-[#FF8F45] text-white whitespace-nowrap">
|
|
719
|
+
{/* <div className="flex items-center gap-[6px] py-[5px] px-[14px] rounded-[38px] text-[12.5px] bg-[#FF8F45] text-white whitespace-nowrap">
|
|
665
720
|
<span className="flex items-center">
|
|
666
721
|
<div className="mb-[2px]">
|
|
667
722
|
<LottiePlayer
|
|
@@ -687,6 +742,44 @@ const FeatureServiceUiMobile = ({
|
|
|
687
742
|
}}
|
|
688
743
|
/>
|
|
689
744
|
</span>
|
|
745
|
+
</div> */}
|
|
746
|
+
<div
|
|
747
|
+
className="flex items-center gap-[6px] py-[5px] px-[14px] rounded-[38px] text-[12.5px] text-white whitespace-nowrap"
|
|
748
|
+
style={{ backgroundColor: "rgba(255, 222, 223, 0.93)" }}
|
|
749
|
+
>
|
|
750
|
+
<span className="flex items-center">
|
|
751
|
+
{serviceItem?.icons?.fireIcon ? (
|
|
752
|
+
<img
|
|
753
|
+
src={serviceItem.icons.fireIcon}
|
|
754
|
+
alt="discount"
|
|
755
|
+
className="h-[12px] w-[12px] object-contain mr-[5px]"
|
|
756
|
+
style={{ filter: isSoldOut ? "grayscale" : "" }}
|
|
757
|
+
/>
|
|
758
|
+
) : null}
|
|
759
|
+
{/* <div className="mb-[2px]">
|
|
760
|
+
<LottiePlayer
|
|
761
|
+
animationData={flameAnimation}
|
|
762
|
+
width="16px"
|
|
763
|
+
height="16px"
|
|
764
|
+
/>
|
|
765
|
+
</div> */}
|
|
766
|
+
<span className="bold-text text-[#464647] ml-[3px]">
|
|
767
|
+
Remate
|
|
768
|
+
</span>{" "}
|
|
769
|
+
<span className="text-[#464647]">términa en </span>{" "}
|
|
770
|
+
<span
|
|
771
|
+
className="bold-text text-end"
|
|
772
|
+
ref={(node) =>
|
|
773
|
+
commonService.startDealCountdown(node, getCountdownSeconds())
|
|
774
|
+
}
|
|
775
|
+
style={{
|
|
776
|
+
fontVariantNumeric: "tabular-nums",
|
|
777
|
+
display: "inline-block",
|
|
778
|
+
color: "#FF5C60",
|
|
779
|
+
minWidth: "50px",
|
|
780
|
+
}}
|
|
781
|
+
/>
|
|
782
|
+
</span>
|
|
690
783
|
</div>
|
|
691
784
|
</div>
|
|
692
785
|
</div>
|
|
@@ -33,6 +33,7 @@ const HARDCODED_OPERATORS = [
|
|
|
33
33
|
seatsAvailable: "3 disponibles",
|
|
34
34
|
},
|
|
35
35
|
];
|
|
36
|
+
const expandedStateMap: Record<string, boolean> = {};
|
|
36
37
|
|
|
37
38
|
const FeatureServiceUi = ({
|
|
38
39
|
serviceItem,
|
|
@@ -155,9 +156,10 @@ const FeatureServiceUi = ({
|
|
|
155
156
|
? activeSlot.label.split(" - ")[1]
|
|
156
157
|
: dealWindowTo;
|
|
157
158
|
|
|
158
|
-
|
|
159
|
-
serviceItem.id
|
|
160
|
-
|
|
159
|
+
if (expandedStateMap[serviceItem.id] === undefined) {
|
|
160
|
+
expandedStateMap[serviceItem.id] = true;
|
|
161
|
+
}
|
|
162
|
+
const isItemExpanded = expandedStateMap[serviceItem.id];
|
|
161
163
|
const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
|
|
162
164
|
const canDecreaseTicketQuantity = ticketQuantity > 1;
|
|
163
165
|
const availableSeats =
|
|
@@ -698,8 +700,38 @@ const FeatureServiceUi = ({
|
|
|
698
700
|
</div>
|
|
699
701
|
|
|
700
702
|
<div
|
|
703
|
+
id={`feature-arrow-${serviceItem.id}`}
|
|
701
704
|
className={`absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`}
|
|
702
|
-
onClick={
|
|
705
|
+
onClick={(e) => {
|
|
706
|
+
expandedStateMap[serviceItem.id] =
|
|
707
|
+
!expandedStateMap[serviceItem.id];
|
|
708
|
+
if (onToggleExpand) onToggleExpand(e);
|
|
709
|
+
|
|
710
|
+
const contentNode = document.getElementById(
|
|
711
|
+
`feature-content-${serviceItem.id}`,
|
|
712
|
+
);
|
|
713
|
+
const innerNode = document.getElementById(
|
|
714
|
+
`feature-inner-content-${serviceItem.id}`,
|
|
715
|
+
);
|
|
716
|
+
const arrowNode = document.getElementById(
|
|
717
|
+
`feature-arrow-${serviceItem.id}`,
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
if (contentNode && innerNode && arrowNode) {
|
|
721
|
+
const isExpanded = expandedStateMap[serviceItem.id];
|
|
722
|
+
if (isExpanded) {
|
|
723
|
+
contentNode.style.gridTemplateRows = "1fr";
|
|
724
|
+
contentNode.style.opacity = "1";
|
|
725
|
+
innerNode.className = `min-h-0 overflow-hidden px-[16px] text-[13.33px] pt-[14px] pb-[6px]`;
|
|
726
|
+
arrowNode.className = `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out rotate-180`;
|
|
727
|
+
} else {
|
|
728
|
+
contentNode.style.gridTemplateRows = "0fr";
|
|
729
|
+
contentNode.style.opacity = "0";
|
|
730
|
+
innerNode.className = `min-h-0 overflow-hidden px-[16px] text-[13.33px] pt-0 pb-0`;
|
|
731
|
+
arrowNode.className = `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out `;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}}
|
|
703
735
|
>
|
|
704
736
|
<img
|
|
705
737
|
src={serviceItem.icons?.downArrow}
|
|
@@ -714,16 +746,18 @@ const FeatureServiceUi = ({
|
|
|
714
746
|
</div>
|
|
715
747
|
</div>
|
|
716
748
|
<div
|
|
749
|
+
id={`feature-content-${serviceItem.id}`}
|
|
717
750
|
className="grid"
|
|
718
751
|
style={{
|
|
719
|
-
gridTemplateRows: "1fr",
|
|
720
|
-
opacity: 1,
|
|
752
|
+
gridTemplateRows: isItemExpanded ? "1fr" : "0fr",
|
|
753
|
+
opacity: isItemExpanded ? 1 : 0,
|
|
721
754
|
transition:
|
|
722
755
|
"grid-template-rows 300ms ease-in-out, opacity 250ms ease-in-out",
|
|
723
756
|
}}
|
|
724
757
|
>
|
|
725
758
|
<div
|
|
726
|
-
|
|
759
|
+
id={`feature-inner-content-${serviceItem.id}`}
|
|
760
|
+
className={`min-h-0 overflow-hidden px-[16px] text-[13.33px] ${isItemExpanded ? "pt-[14px] pb-[6px]" : "pt-0 pb-0"}`}
|
|
727
761
|
style={{ transition: "padding 300ms ease-in-out" }}
|
|
728
762
|
>
|
|
729
763
|
<span className="bold-text">¿Cómo funciona?</span>
|