kupos-ui-components-lib 10.4.75 → 10.4.77
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 -0
- package/dist/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.js +173 -108
- package/dist/ui/FeatureServiceUI/FeatureServiceUi.js +188 -139
- package/dist/ui/FeatureServiceUI/featureServiceShared.d.ts +20 -0
- package/dist/ui/FeatureServiceUI/featureServiceShared.js +66 -0
- package/package.json +1 -1
- package/src/ui/FeaturServiceUiMobile/FeatureServiceUiMobile.tsx +290 -70
- package/src/ui/FeatureServiceUI/FeatureServiceUi.tsx +321 -119
- package/src/ui/FeatureServiceUI/featureServiceShared.tsx +123 -0
package/dist/styles.css
CHANGED
|
@@ -270,6 +270,9 @@
|
|
|
270
270
|
.mt-\[50px\] {
|
|
271
271
|
margin-top: 50px;
|
|
272
272
|
}
|
|
273
|
+
.mt-auto {
|
|
274
|
+
margin-top: auto;
|
|
275
|
+
}
|
|
273
276
|
.-mr-\[12px\] {
|
|
274
277
|
margin-right: calc(12px * -1);
|
|
275
278
|
}
|
|
@@ -1106,6 +1109,9 @@
|
|
|
1106
1109
|
.py-\[2px\] {
|
|
1107
1110
|
padding-block: 2px;
|
|
1108
1111
|
}
|
|
1112
|
+
.py-\[3px\] {
|
|
1113
|
+
padding-block: 3px;
|
|
1114
|
+
}
|
|
1109
1115
|
.py-\[4px\] {
|
|
1110
1116
|
padding-block: 4px;
|
|
1111
1117
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import LottiePlayer from "../../assets/LottiePlayer";
|
|
3
3
|
import commonService from "../../utils/CommonService";
|
|
4
|
+
import { CARD_BORDER, CARD_SELECTED_BORDER, CARD_SELECTED_SHADOW, CalendarIcon, CrownIcon, DestinationIcon, OriginIcon, formatDepartureTime, formatTravelDate, setNodeText, } from "../FeatureServiceUI/featureServiceShared";
|
|
4
5
|
const HARDCODED_OPERATORS = [
|
|
5
6
|
{
|
|
6
7
|
logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
|
|
@@ -21,8 +22,12 @@ const HARDCODED_OPERATORS = [
|
|
|
21
22
|
seatsAvailable: "3 disponibles",
|
|
22
23
|
},
|
|
23
24
|
];
|
|
25
|
+
// Selected operator card per serviceItem.id (no hooks — module-level state)
|
|
26
|
+
const selectedCardMap = {};
|
|
27
|
+
// Whether the "select a card" error is visible per serviceItem.id
|
|
28
|
+
const selectErrorMap = {};
|
|
24
29
|
const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity = 1, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData = undefined, }) => {
|
|
25
|
-
var _a, _b, _c, _d, _e
|
|
30
|
+
var _a, _b, _c, _d, _e;
|
|
26
31
|
// Use wow_deal data if available, otherwise fall back to serviceItem operators or hardcoded
|
|
27
32
|
const operators = ((_a = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.services) === null || _a === void 0 ? void 0 : _a.length) > 0
|
|
28
33
|
? wowDealData.services.slice(0, 3).map((service) => ({
|
|
@@ -69,6 +74,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
69
74
|
const activeSlot = availableTimeSlots.find((s) => s.label === selectedTimeSlot) ||
|
|
70
75
|
availableTimeSlots[0];
|
|
71
76
|
// Services that fall within the active slot, sorted by final price ascending
|
|
77
|
+
// (only used by the disabled time slot dropdown)
|
|
72
78
|
const servicesInActiveSlot = activeSlot
|
|
73
79
|
? services
|
|
74
80
|
.filter((s) => {
|
|
@@ -77,28 +83,90 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
77
83
|
})
|
|
78
84
|
.sort((a, b) => a.final_price - b.final_price)
|
|
79
85
|
: services;
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
// All services in the deal window (time slot dropdown is disabled),
|
|
87
|
+
// cheapest first, then by departure time.
|
|
88
|
+
const sortedServices = [...services].sort((a, b) => {
|
|
89
|
+
var _a, _b;
|
|
90
|
+
return ((_a = a.final_price) !== null && _a !== void 0 ? _a : 0) - ((_b = b.final_price) !== null && _b !== void 0 ? _b : 0) ||
|
|
91
|
+
commonService.timeToMinutes(a.departure_time) -
|
|
92
|
+
commonService.timeToMinutes(b.departure_time);
|
|
93
|
+
});
|
|
94
|
+
// Cards — 3 visible, the rest scroll horizontally (scrollbar hidden)
|
|
95
|
+
const serviceCards = sortedServices.length > 0
|
|
96
|
+
? sortedServices.map((s, idx) => {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
return ({
|
|
99
|
+
key: String((_b = (_a = s === null || s === void 0 ? void 0 : s.id) !== null && _a !== void 0 ? _a : s === null || s === void 0 ? void 0 : s.service_id) !== null && _b !== void 0 ? _b : `${s === null || s === void 0 ? void 0 : s.operator_name}-${s === null || s === void 0 ? void 0 : s.departure_time}-${idx}`),
|
|
100
|
+
logo: s.operator_logo_url,
|
|
101
|
+
name: s.operator_name,
|
|
102
|
+
time: s.departure_time,
|
|
103
|
+
price: s.final_price,
|
|
104
|
+
seatsAvailable: `¡Solo quedan ${s.available_seats}!`,
|
|
105
|
+
service: s,
|
|
106
|
+
});
|
|
107
|
+
})
|
|
108
|
+
: operators.map((op, idx) => {
|
|
109
|
+
var _a;
|
|
110
|
+
return (Object.assign(Object.assign({}, op), { key: `fallback-${idx}`, price: (_a = op.price) !== null && _a !== void 0 ? _a : finalPrice, service: null }));
|
|
111
|
+
});
|
|
112
|
+
const hasCardScroll = serviceCards.length > 3;
|
|
113
|
+
// Default to the first (cheapest) card when nothing is selected yet,
|
|
114
|
+
// or when the previous selection is no longer in the list (data refresh)
|
|
115
|
+
if (serviceCards.length > 0 &&
|
|
116
|
+
!serviceCards.some((c) => c.key === selectedCardMap[serviceItem.id])) {
|
|
117
|
+
selectedCardMap[serviceItem.id] = serviceCards[0].key;
|
|
118
|
+
}
|
|
119
|
+
const selectedCard = serviceCards.find((c) => c.key === selectedCardMap[serviceItem.id]);
|
|
120
|
+
const isSelectErrorVisible = !selectedCard && !!selectErrorMap[serviceItem.id];
|
|
121
|
+
const getPriceInfo = (svc) => svc
|
|
122
|
+
? {
|
|
123
|
+
final: svc.final_price,
|
|
124
|
+
original: svc.original_price,
|
|
125
|
+
savings: svc.original_price
|
|
126
|
+
? Math.round(((svc.original_price - svc.final_price) / svc.original_price) *
|
|
127
|
+
100)
|
|
128
|
+
: savingsPercent,
|
|
129
|
+
}
|
|
130
|
+
: { final: finalPrice, original: originalPrice, savings: savingsPercent };
|
|
131
|
+
// Price shown: the selected card's service, otherwise the cheapest service
|
|
132
|
+
const selectedSlotService = (selectedCard === null || selectedCard === void 0 ? void 0 : selectedCard.service) || sortedServices[0];
|
|
133
|
+
const { final: displayFinalPrice, original: displayOriginalPrice, savings: displaySavingsPercent, } = getPriceInfo(selectedSlotService);
|
|
134
|
+
const handleCardSelect = (key) => {
|
|
135
|
+
selectedCardMap[serviceItem.id] = key;
|
|
136
|
+
selectErrorMap[serviceItem.id] = false;
|
|
137
|
+
if (typeof document === "undefined")
|
|
138
|
+
return;
|
|
139
|
+
const container = document.getElementById(`feature-m-cards-${serviceItem.id}`);
|
|
140
|
+
container === null || container === void 0 ? void 0 : container.querySelectorAll("[data-card-key]").forEach((node) => {
|
|
141
|
+
const isSelected = node.dataset.cardKey === key;
|
|
142
|
+
node.style.border = isSelected ? CARD_SELECTED_BORDER : CARD_BORDER;
|
|
143
|
+
node.style.boxShadow = isSelected ? CARD_SELECTED_SHADOW : "none";
|
|
144
|
+
// Crown stays on the lowest fare card — selection only moves the border
|
|
145
|
+
});
|
|
146
|
+
const card = serviceCards.find((c) => c.key === key);
|
|
147
|
+
const price = getPriceInfo((card === null || card === void 0 ? void 0 : card.service) || sortedServices[0]);
|
|
148
|
+
setNodeText(document.getElementById(`feature-m-final-price-${serviceItem.id}`), commonService.currencyRounded(price.final * ticketQuantity));
|
|
149
|
+
setNodeText(document.getElementById(`feature-m-original-price-${serviceItem.id}`), commonService.currencyRounded(price.original * ticketQuantity));
|
|
150
|
+
document
|
|
151
|
+
.querySelectorAll(`[data-feature-m-savings="${serviceItem.id}"]`)
|
|
152
|
+
.forEach((node) => setNodeText(node, String(price.savings)));
|
|
153
|
+
const errorNode = document.getElementById(`feature-m-select-error-${serviceItem.id}`);
|
|
154
|
+
if (errorNode)
|
|
155
|
+
errorNode.style.display = "none";
|
|
156
|
+
};
|
|
94
157
|
// The label shown on the dropdown button (24h format, e.g. "Entre 16:00 y 20:00")
|
|
95
158
|
const departureRange = (activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label) || `Entre ${dealWindowFrom} y ${dealWindowTo}`;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
159
|
+
// Time slot dropdown is disabled, so use the full deal window
|
|
160
|
+
const currentWindowFrom = dealWindowFrom;
|
|
161
|
+
const currentWindowTo = dealWindowTo;
|
|
162
|
+
// const currentWindowFrom = activeSlot
|
|
163
|
+
// ? commonService.minutesToTime(activeSlot.start)
|
|
164
|
+
// : dealWindowFrom;
|
|
165
|
+
// const currentWindowTo = activeSlot
|
|
166
|
+
// ? commonService.minutesToTime(
|
|
167
|
+
// activeSlot.end === 1440 ? 1439 : activeSlot.end,
|
|
168
|
+
// )
|
|
169
|
+
// : dealWindowTo;
|
|
102
170
|
const isItemExpanded = serviceItem.id === isFeatureDropDownExpand ||
|
|
103
171
|
isFeatureDropDownExpand === true;
|
|
104
172
|
const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
|
|
@@ -171,97 +239,72 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
171
239
|
fontSize: "10px",
|
|
172
240
|
} },
|
|
173
241
|
React.createElement("span", null,
|
|
174
|
-
"AHORRAS
|
|
175
|
-
|
|
242
|
+
"AHORRAS",
|
|
243
|
+
" ",
|
|
244
|
+
React.createElement("span", { "data-feature-m-savings": serviceItem.id }, displaySavingsPercent),
|
|
176
245
|
"%")))),
|
|
177
246
|
React.createElement("div", { id: `service-card-${serviceItem.id}`, className: "bg-[#0C1421] text-white mx-auto relative rounded-[14px] p-[14px] text-[13.33px]" },
|
|
178
247
|
React.createElement("div", { className: "flex flex-col gap-[10px]" },
|
|
179
248
|
React.createElement("div", { className: " text-[white]" },
|
|
180
|
-
React.createElement("div", { className: "flex flex-col gap-[
|
|
181
|
-
React.createElement("div", { className: "flex items-
|
|
182
|
-
React.createElement(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
React.createElement("span", { className: "text-[14px] bold-text" }, "\u2192"),
|
|
196
|
-
React.createElement("span", { className: "text-[14px] bold-text" }, cityDestination === null || cityDestination === void 0 ? void 0 : cityDestination.label.split(",")[0])),
|
|
197
|
-
React.createElement("div", { className: "kupos-time-dd relative", tabIndex: 0, onBlur: (e) => {
|
|
198
|
-
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
199
|
-
onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
|
|
200
|
-
}
|
|
201
|
-
}, style: { outline: "none", marginTop: "3px" } },
|
|
202
|
-
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]" },
|
|
203
|
-
React.createElement("span", null, departureRange),
|
|
204
|
-
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: {
|
|
205
|
-
width: "12px",
|
|
206
|
-
height: "8px",
|
|
207
|
-
filter: "brightness(0) invert(1)",
|
|
208
|
-
} })),
|
|
209
|
-
isThisTimeDropdownOpen && (React.createElement(React.Fragment, null,
|
|
210
|
-
React.createElement("div", { className: "absolute left-0 top-[calc(100%+10px)]", style: {
|
|
211
|
-
zIndex: 20,
|
|
212
|
-
backgroundColor: "#fff",
|
|
213
|
-
borderRadius: "14px",
|
|
214
|
-
minWidth: "190px",
|
|
215
|
-
boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
|
|
216
|
-
overflow: "hidden",
|
|
217
|
-
padding: "6px 0",
|
|
218
|
-
} }, availableTimeSlots.map((slot) => {
|
|
219
|
-
const isActive = slot.label === selectedTimeSlot ||
|
|
220
|
-
slot === activeSlot;
|
|
221
|
-
// Count services in this slot
|
|
222
|
-
const count = services.filter((s) => {
|
|
223
|
-
const depMin = commonService.timeToMinutes(s.departure_time);
|
|
224
|
-
return (depMin >= slot.start && depMin < slot.end);
|
|
225
|
-
}).length;
|
|
226
|
-
return (React.createElement("button", { key: slot.label, type: "button", onClick: () => {
|
|
227
|
-
onTimeSlotChange === null || onTimeSlotChange === void 0 ? void 0 : onTimeSlotChange(slot.label);
|
|
228
|
-
onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
|
|
229
|
-
}, 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]"}` },
|
|
230
|
-
React.createElement("span", null, slot.label),
|
|
231
|
-
count > 0 && (React.createElement("span", { className: `text-[11px] rounded-full px-[6px] py-[2px] font-bold ${isActive
|
|
232
|
-
? "bg-[white] text-[#FF5C60]"
|
|
233
|
-
: "bg-[#FF5C60] text-[white]"}` }, count))));
|
|
234
|
-
}))))))))),
|
|
249
|
+
React.createElement("div", { className: "flex flex-col gap-[10px]", style: { filter: isSoldOut ? "grayscale(1)" : undefined } },
|
|
250
|
+
React.createElement("div", { className: "flex items-center gap-[10px]" },
|
|
251
|
+
React.createElement(OriginIcon, { size: 16 }),
|
|
252
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, cityOrigin === null || cityOrigin === void 0 ? void 0 : cityOrigin.label.split(",")[0])),
|
|
253
|
+
React.createElement("div", { className: "flex items-center gap-[10px]" },
|
|
254
|
+
React.createElement(DestinationIcon, { size: 16 }),
|
|
255
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, cityDestination === null || cityDestination === void 0 ? void 0 : cityDestination.label.split(",")[0])),
|
|
256
|
+
React.createElement("div", { className: "flex items-center gap-[10px] text-[13px]" },
|
|
257
|
+
React.createElement(CalendarIcon, { size: 16 }),
|
|
258
|
+
React.createElement("span", null, formatTravelDate(travelDate)))),
|
|
259
|
+
React.createElement("div", { className: "flex items-center gap-[10px] mt-[14px]" },
|
|
260
|
+
React.createElement("div", { className: "shrink-0" }, renderIcon("sheildIcon", "20px")),
|
|
261
|
+
React.createElement("div", { className: "flex flex-col gap-[2px]" },
|
|
262
|
+
React.createElement("span", { className: "bold-text text-[13px] text-white" }, "Precio Remate"),
|
|
263
|
+
React.createElement("span", { className: "text-[11px] text-[#dcdcdc]" }, "Sin cambios, sin cancelaci\u00F3n")))),
|
|
235
264
|
React.createElement("div", { className: "border-t border-[#363c48] my-[8px]" }),
|
|
236
265
|
React.createElement("div", null,
|
|
237
|
-
React.createElement("span", { className: "block w-full text-[14px] bold-text text-[white] mb-[
|
|
238
|
-
|
|
239
|
-
?
|
|
266
|
+
React.createElement("span", { className: "block w-full text-[14px] bold-text text-[white] mb-[2px]", style: { textAlign: "center" } },
|
|
267
|
+
sortedServices.length > 0
|
|
268
|
+
? sortedServices.length
|
|
240
269
|
: operatorsCompetingCount,
|
|
241
270
|
" ",
|
|
242
|
-
"
|
|
271
|
+
"ofertas compitiendo ",
|
|
243
272
|
React.createElement("br", null),
|
|
244
273
|
"por tu compra"),
|
|
245
|
-
React.createElement("div", { className: "flex gap-[8px] text-[white]", style: {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
274
|
+
React.createElement("div", { id: `feature-m-cards-${serviceItem.id}`, className: "feature-cards-scroll flex w-full items-stretch gap-[8px] overflow-x-auto text-[white]", style: {
|
|
275
|
+
justifyContent: hasCardScroll ? "flex-start" : "center",
|
|
276
|
+
// room for the crown badge that sits above the selected card
|
|
277
|
+
paddingTop: "12px",
|
|
278
|
+
scrollbarWidth: "none",
|
|
279
|
+
} }, serviceCards.map((op, idx) => {
|
|
280
|
+
const isSelected = op.key === (selectedCard === null || selectedCard === void 0 ? void 0 : selectedCard.key);
|
|
281
|
+
// Cards are sorted cheapest first, so index 0 is the lowest fare
|
|
282
|
+
const isLowestFare = idx === 0;
|
|
283
|
+
return (React.createElement("div", { key: op.key, "data-card-key": op.key, role: "button", tabIndex: 0, "aria-pressed": isSelected, onClick: () => handleCardSelect(op.key), onKeyDown: (e) => {
|
|
284
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
285
|
+
e.preventDefault();
|
|
286
|
+
handleCardSelect(op.key);
|
|
287
|
+
}
|
|
288
|
+
}, className: "relative flex min-w-0 cursor-pointer flex-col items-center justify-center gap-[6px] outline-none", style: {
|
|
289
|
+
flex: "0 0 calc((100% - 16px) / 3)",
|
|
290
|
+
minWidth: "90px",
|
|
291
|
+
border: isSelected ? CARD_SELECTED_BORDER : CARD_BORDER,
|
|
292
|
+
boxShadow: isSelected ? CARD_SELECTED_SHADOW : "none",
|
|
262
293
|
backgroundColor: "#1a202e",
|
|
263
|
-
padding: "
|
|
294
|
+
padding: "16px 6px 12px",
|
|
295
|
+
borderRadius: "10px",
|
|
296
|
+
transition: "border-color 150ms ease, box-shadow 150ms ease",
|
|
264
297
|
} },
|
|
298
|
+
React.createElement("span", { className: "absolute items-center justify-center", style: {
|
|
299
|
+
display: isLowestFare ? "flex" : "none",
|
|
300
|
+
top: "-12px",
|
|
301
|
+
right: "-1px",
|
|
302
|
+
width: "30px",
|
|
303
|
+
height: "20px",
|
|
304
|
+
borderRadius: "6px",
|
|
305
|
+
backgroundColor: "#FF5C60",
|
|
306
|
+
} },
|
|
307
|
+
React.createElement(CrownIcon, { width: 14, height: 11 })),
|
|
265
308
|
React.createElement("img", { src: op.logo, alt: op.name, onError: (e) => {
|
|
266
309
|
var _a;
|
|
267
310
|
e.currentTarget.src =
|
|
@@ -272,8 +315,10 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
272
315
|
maxWidth: "100%",
|
|
273
316
|
} }),
|
|
274
317
|
React.createElement("span", { className: "text-[12px] truncate max-w-full text-center" }, op.name),
|
|
275
|
-
React.createElement("
|
|
276
|
-
|
|
318
|
+
React.createElement("div", { className: "bg-[#FF8F45] text-white text-[12px] font-bold px-[10px] py-[3px] rounded-[6px] bold-text whitespace-nowrap" }, commonService.currencyRounded(op.price)),
|
|
319
|
+
React.createElement("span", { className: "bold-text text-[12px] whitespace-nowrap" }, formatDepartureTime(op === null || op === void 0 ? void 0 : op.time)),
|
|
320
|
+
React.createElement("span", { className: "bold-text text-[11px] whitespace-nowrap" }, op === null || op === void 0 ? void 0 : op.seatsAvailable)));
|
|
321
|
+
})),
|
|
277
322
|
React.createElement("div", { className: "flex w-full items-center justify-center gap-[6px] text-[12px] mt-[12px]", style: {
|
|
278
323
|
padding: "4px 14px",
|
|
279
324
|
} },
|
|
@@ -320,7 +365,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
320
365
|
} },
|
|
321
366
|
React.createElement("div", { className: "flex flex-col" },
|
|
322
367
|
React.createElement("span", { className: "text-[18px] font-normal leading-[20px] text-[#9f9f9f] relative", style: { position: "relative" } },
|
|
323
|
-
commonService.currencyRounded(displayOriginalPrice * ticketQuantity),
|
|
368
|
+
React.createElement("span", { id: `feature-m-original-price-${serviceItem.id}` }, commonService.currencyRounded(displayOriginalPrice * ticketQuantity)),
|
|
324
369
|
React.createElement("span", { style: {
|
|
325
370
|
position: "absolute",
|
|
326
371
|
left: "-2px",
|
|
@@ -331,12 +376,12 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
331
376
|
transform: "rotate(-10deg)",
|
|
332
377
|
transformOrigin: "center",
|
|
333
378
|
} })),
|
|
334
|
-
React.createElement("span", { className: "text-[white] bold-text text-[24px] leading-none mt-[4px]" }, commonService.currencyRounded(displayFinalPrice * ticketQuantity))),
|
|
379
|
+
React.createElement("span", { id: `feature-m-final-price-${serviceItem.id}`, className: "text-[white] bold-text text-[24px] leading-none mt-[4px]" }, commonService.currencyRounded(displayFinalPrice * ticketQuantity))),
|
|
335
380
|
React.createElement("span", { className: "text-[#FF8F45] bold-text text-[22px] leading-tight", style: {
|
|
336
381
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
337
382
|
whiteSpace: "nowrap",
|
|
338
383
|
} },
|
|
339
|
-
displaySavingsPercent,
|
|
384
|
+
React.createElement("span", { "data-feature-m-savings": serviceItem.id }, displaySavingsPercent),
|
|
340
385
|
"% OFF")),
|
|
341
386
|
React.createElement("button", { type: "button", ref: (btn) => {
|
|
342
387
|
if (!btn)
|
|
@@ -344,6 +389,23 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
344
389
|
btn.onclick = () => {
|
|
345
390
|
if (btn.disabled)
|
|
346
391
|
return;
|
|
392
|
+
// Require an operator card to be selected before booking
|
|
393
|
+
const currentCard = serviceCards.find((c) => c.key === selectedCardMap[serviceItem.id]);
|
|
394
|
+
if (!currentCard) {
|
|
395
|
+
selectErrorMap[serviceItem.id] = true;
|
|
396
|
+
const errorNode = document.getElementById(`feature-m-select-error-${serviceItem.id}`);
|
|
397
|
+
if (errorNode)
|
|
398
|
+
errorNode.style.display = "block";
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
const chosenService = currentCard.service;
|
|
402
|
+
// Selected service first so consumers reading [0] get the chosen one
|
|
403
|
+
const orderedServices = chosenService
|
|
404
|
+
? [
|
|
405
|
+
chosenService,
|
|
406
|
+
...sortedServices.filter((s) => s !== chosenService),
|
|
407
|
+
]
|
|
408
|
+
: sortedServices;
|
|
347
409
|
btn.disabled = true;
|
|
348
410
|
btn.style.cursor = "not-allowed";
|
|
349
411
|
btn.style.opacity = "0.85";
|
|
@@ -353,7 +415,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
353
415
|
normal.style.display = "none";
|
|
354
416
|
if (loading)
|
|
355
417
|
loading.style.display = "flex";
|
|
356
|
-
onBookButtonPress === null || onBookButtonPress === void 0 ? void 0 : onBookButtonPress(ticketQuantity, serviceItem,
|
|
418
|
+
onBookButtonPress === null || onBookButtonPress === void 0 ? void 0 : onBookButtonPress(ticketQuantity, serviceItem, orderedServices, chosenService);
|
|
357
419
|
};
|
|
358
420
|
}, 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", style: {
|
|
359
421
|
backgroundColor: "#FF5C60",
|
|
@@ -362,8 +424,11 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
362
424
|
} },
|
|
363
425
|
React.createElement(LottiePlayer, { animationData: serviceItem.icons.thunderAnim, width: "16px", height: "16px" }),
|
|
364
426
|
React.createElement("span", { className: "whitespace-nowrap" }, "\u00A1Lo quiero!")),
|
|
427
|
+
React.createElement("span", { id: `feature-m-select-error-${serviceItem.id}`, role: "alert", className: "text-[11px] text-[#FF5C60] text-center leading-[14px] mt-[6px]", style: {
|
|
428
|
+
display: isSelectErrorVisible ? "block" : "none",
|
|
429
|
+
} }, "Selecciona un horario para continuar"),
|
|
365
430
|
React.createElement("div", { className: "flex justify-end mt-[10px]", onClick: onToggleExpand },
|
|
366
|
-
React.createElement("img", { src: (
|
|
431
|
+
React.createElement("img", { src: (_d = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _d === void 0 ? void 0 : _d.downArrow, alt: "down arrow", className: `transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`, style: {
|
|
367
432
|
width: "14px",
|
|
368
433
|
height: "8px",
|
|
369
434
|
filter: "brightness(0) invert(1)",
|
|
@@ -384,7 +449,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
384
449
|
React.createElement("div", { className: "absolute -top-[13px] left-0 w-full flex items-center justify-center gap-[12px] z-10 " },
|
|
385
450
|
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)" } },
|
|
386
451
|
React.createElement("span", { className: "flex items-center" },
|
|
387
|
-
((
|
|
452
|
+
((_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _e === void 0 ? void 0 : _e.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[12px] w-[12px] object-contain mr-[5px]", style: { filter: isSoldOut ? "grayscale" : "" } })) : null,
|
|
388
453
|
React.createElement("span", { className: "bold-text text-[#464647] ml-[3px]" }, "Remate\u00A0"),
|
|
389
454
|
" ",
|
|
390
455
|
React.createElement("span", { className: "text-[#464647]" }, "t\u00E9rmina en\u00A0"),
|