kupos-ui-components-lib 10.4.5 → 10.4.6
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
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
|
}
|
|
@@ -92,8 +92,26 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
92
92
|
selectedSlotService.original_price) *
|
|
93
93
|
100)
|
|
94
94
|
: savingsPercent;
|
|
95
|
+
// Convert "HH:MM" (24h) → "H:MM AM/PM"
|
|
96
|
+
const formatTimeAmPm = (time) => {
|
|
97
|
+
const match = time.match(/(\d{1,2}):(\d{2})/);
|
|
98
|
+
if (!match)
|
|
99
|
+
return time;
|
|
100
|
+
let h = parseInt(match[1], 10);
|
|
101
|
+
const m = match[2];
|
|
102
|
+
const period = h >= 12 ? "PM" : "AM";
|
|
103
|
+
h = h % 12 || 12;
|
|
104
|
+
return `${h}:${m} ${period}`;
|
|
105
|
+
};
|
|
106
|
+
// Convert all HH:MM occurrences in a label string to AM/PM
|
|
107
|
+
const formatLabelAmPm = (label) => label.replace(/(\d{1,2}):(\d{2})/g, (_, hh, mm) => {
|
|
108
|
+
let h = parseInt(hh, 10);
|
|
109
|
+
const period = h >= 12 ? "PM" : "AM";
|
|
110
|
+
h = h % 12 || 12;
|
|
111
|
+
return `${h}:${mm} ${period}`;
|
|
112
|
+
});
|
|
95
113
|
// The label shown on the dropdown button
|
|
96
|
-
const departureRange = (activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label) || `Entre ${dealWindowFrom} y ${dealWindowTo}
|
|
114
|
+
const departureRange = formatLabelAmPm((activeSlot === null || activeSlot === void 0 ? void 0 : activeSlot.label) || `Entre ${dealWindowFrom} y ${dealWindowTo}`);
|
|
97
115
|
const isItemExpanded = serviceItem.id === isFeatureDropDownExpand ||
|
|
98
116
|
isFeatureDropDownExpand === true;
|
|
99
117
|
const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
|
|
@@ -159,7 +177,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
159
177
|
React.createElement("div", { className: "bold-text font-[9px]", style: {
|
|
160
178
|
backgroundColor: "#FF5C60",
|
|
161
179
|
padding: "4px 8px",
|
|
162
|
-
borderRadius: "
|
|
180
|
+
borderRadius: "6px",
|
|
163
181
|
color: "#fff",
|
|
164
182
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
165
183
|
whiteSpace: "nowrap",
|
|
@@ -172,68 +190,77 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
172
190
|
React.createElement("div", { id: `service-card-${serviceItem.id}`, className: "bg-[#0C1421] text-white mx-auto relative rounded-[14px] p-[14px] text-[13.33px]" },
|
|
173
191
|
React.createElement("div", { className: "flex flex-col gap-[10px]" },
|
|
174
192
|
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
|
-
|
|
193
|
+
React.createElement("div", { className: "flex flex-col gap-[6px] relative" },
|
|
194
|
+
React.createElement("div", { className: "flex items-start gap-[10px]" },
|
|
195
|
+
React.createElement("div", { className: "flex flex-col items-center shrink-0", style: { paddingTop: "2px" } },
|
|
196
|
+
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" : ""}` }),
|
|
197
|
+
React.createElement("div", { style: {
|
|
198
|
+
width: "1.5px",
|
|
199
|
+
flex: 1,
|
|
200
|
+
minHeight: "8px",
|
|
201
|
+
backgroundColor: "rgba(255,255,255,0.35)",
|
|
202
|
+
margin: "2px 0",
|
|
203
|
+
} }),
|
|
204
|
+
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 } })),
|
|
205
|
+
React.createElement("div", { className: "flex flex-col gap-[6px]" },
|
|
206
|
+
React.createElement("div", { className: "flex items-center gap-[6px]" },
|
|
207
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, cityOrigin === null || cityOrigin === void 0 ? void 0 : cityOrigin.label.split(",")[0]),
|
|
208
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, "\u2192"),
|
|
209
|
+
React.createElement("span", { className: "text-[14px] bold-text" }, cityDestination === null || cityDestination === void 0 ? void 0 : cityDestination.label.split(",")[0])),
|
|
210
|
+
React.createElement("div", { className: "kupos-time-dd relative", tabIndex: 0, onBlur: (e) => {
|
|
211
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
212
|
+
onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
|
|
213
|
+
}
|
|
214
|
+
}, style: { outline: "none", marginTop: "3px" } },
|
|
215
|
+
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]" },
|
|
216
|
+
React.createElement("span", null, departureRange),
|
|
217
|
+
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: {
|
|
218
|
+
width: "12px",
|
|
219
|
+
height: "8px",
|
|
220
|
+
filter: "brightness(0) invert(1)",
|
|
221
|
+
} })),
|
|
222
|
+
isThisTimeDropdownOpen && (React.createElement(React.Fragment, null,
|
|
223
|
+
React.createElement("div", { className: "absolute left-0 top-[calc(100%+10px)]", style: {
|
|
224
|
+
zIndex: 20,
|
|
225
|
+
backgroundColor: "#fff",
|
|
226
|
+
borderRadius: "14px",
|
|
227
|
+
minWidth: "190px",
|
|
228
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
|
|
229
|
+
overflow: "hidden",
|
|
230
|
+
padding: "6px 0",
|
|
231
|
+
} }, availableTimeSlots.map((slot) => {
|
|
232
|
+
const isActive = slot.label === selectedTimeSlot ||
|
|
233
|
+
slot === activeSlot;
|
|
234
|
+
// Count services in this slot
|
|
235
|
+
const count = services.filter((s) => {
|
|
236
|
+
const depMin = commonService.timeToMinutes(s.departure_time);
|
|
237
|
+
return (depMin >= slot.start && depMin < slot.end);
|
|
238
|
+
}).length;
|
|
239
|
+
return (React.createElement("button", { key: slot.label, type: "button", onClick: () => {
|
|
240
|
+
onTimeSlotChange === null || onTimeSlotChange === void 0 ? void 0 : onTimeSlotChange(slot.label);
|
|
241
|
+
onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
|
|
242
|
+
// const slotServices = services.filter((s) => {
|
|
243
|
+
// const depMin = commonService.timeToMinutes(
|
|
244
|
+
// s.departure_time,
|
|
245
|
+
// );
|
|
246
|
+
// return (
|
|
247
|
+
// depMin >= slot.start && depMin < slot.end
|
|
248
|
+
// );
|
|
249
|
+
// });
|
|
250
|
+
// console.log(
|
|
251
|
+
// "Selected slot label:",
|
|
252
|
+
// slot.label,
|
|
253
|
+
// );
|
|
254
|
+
// console.log(
|
|
255
|
+
// "Services in selected slot:",
|
|
256
|
+
// slotServices,
|
|
257
|
+
// );
|
|
258
|
+
}, 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]"}` },
|
|
259
|
+
React.createElement("span", null, slot.label),
|
|
260
|
+
count > 0 && (React.createElement("span", { className: `text-[11px] rounded-full px-[6px] py-[2px] font-bold ${isActive
|
|
261
|
+
? "bg-[white] text-[#FF5C60]"
|
|
262
|
+
: "bg-[#FF5C60] text-[white]"}` }, count))));
|
|
263
|
+
}))))))))),
|
|
237
264
|
React.createElement("div", { className: "border-t border-[#363c48] my-[8px]" }),
|
|
238
265
|
React.createElement("div", null,
|
|
239
266
|
React.createElement("span", { className: "block w-full text-[14px] bold-text text-[white] mb-[10px]", style: { textAlign: "center" } },
|
|
@@ -244,29 +271,38 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
|
|
|
244
271
|
"operadores compitiendo ",
|
|
245
272
|
React.createElement("br", null),
|
|
246
273
|
"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
|
-
|
|
274
|
+
React.createElement("div", { className: "flex gap-[8px] text-[white]", style: { width: "100%", justifyContent: "center" } }, (() => {
|
|
275
|
+
const displayOps = servicesInActiveSlot.length > 0
|
|
276
|
+
? servicesInActiveSlot.slice(0, 3).map((s) => ({
|
|
277
|
+
logo: s.operator_logo_url,
|
|
278
|
+
name: s.operator_name,
|
|
279
|
+
time: s.departure_time,
|
|
280
|
+
seatsAvailable: `${s.available_seats} disponibles`,
|
|
281
|
+
}))
|
|
282
|
+
: operators;
|
|
283
|
+
const isSingle = displayOps.length === 1;
|
|
284
|
+
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: {
|
|
285
|
+
flex: isSingle ? "none" : 1,
|
|
286
|
+
width: isSingle ? "32%" : undefined,
|
|
287
|
+
margin: isSingle ? "0 auto" : undefined,
|
|
288
|
+
minWidth: 0,
|
|
289
|
+
height: "100px",
|
|
290
|
+
border: "1px solid #363c48",
|
|
291
|
+
backgroundColor: "#1a202e",
|
|
292
|
+
padding: "14px 6px",
|
|
293
|
+
} },
|
|
294
|
+
React.createElement("img", { src: op.logo, alt: op.name, onError: (e) => {
|
|
295
|
+
var _a;
|
|
296
|
+
e.currentTarget.src =
|
|
297
|
+
((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_details) === null || _a === void 0 ? void 0 : _a[0]) ||
|
|
298
|
+
"/images/service-list/bus-icon.svg";
|
|
299
|
+
}, className: `object-contain ${isSoldOut ? "grayscale" : ""}`, style: {
|
|
300
|
+
height: "24px",
|
|
301
|
+
maxWidth: "100%",
|
|
302
|
+
} }),
|
|
303
|
+
React.createElement("span", { className: "text-[12px] truncate max-w-full text-center" }, op.name),
|
|
304
|
+
React.createElement("span", { className: "text-[11px] whitespace-nowrap" }, op === null || op === void 0 ? void 0 : op.seatsAvailable))));
|
|
305
|
+
})()),
|
|
270
306
|
React.createElement("div", { className: "flex w-full items-center justify-center gap-[6px] text-[12px] mt-[12px]", style: {
|
|
271
307
|
padding: "4px 14px",
|
|
272
308
|
} },
|
package/package.json
CHANGED
|
@@ -131,9 +131,30 @@ 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
|
+
);
|
|
137
158
|
|
|
138
159
|
const isItemExpanded =
|
|
139
160
|
serviceItem.id === isFeatureDropDownExpand ||
|
|
@@ -213,7 +234,7 @@ const FeatureServiceUiMobile = ({
|
|
|
213
234
|
style={{
|
|
214
235
|
backgroundColor: "#FF5C60",
|
|
215
236
|
padding: "4px 8px",
|
|
216
|
-
borderRadius: "
|
|
237
|
+
borderRadius: "6px",
|
|
217
238
|
color: "#fff",
|
|
218
239
|
animation: "pulse-zoom 2s ease-in-out infinite",
|
|
219
240
|
whiteSpace: "nowrap",
|
|
@@ -230,141 +251,160 @@ const FeatureServiceUiMobile = ({
|
|
|
230
251
|
>
|
|
231
252
|
<div className="flex flex-col gap-[10px]">
|
|
232
253
|
<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]">
|
|
254
|
+
<div className="flex flex-col gap-[6px] relative">
|
|
255
|
+
{/* Origin / Destination row with connecting-line icons */}
|
|
256
|
+
<div className="flex items-start gap-[10px]">
|
|
257
|
+
{/* Icon column with connecting line */}
|
|
264
258
|
<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" }}
|
|
259
|
+
className="flex flex-col items-center shrink-0"
|
|
260
|
+
style={{ paddingTop: "2px" }}
|
|
273
261
|
>
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
262
|
+
<img
|
|
263
|
+
src={serviceItem.icons?.whiteOrigin}
|
|
264
|
+
alt="origin"
|
|
265
|
+
className={`w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}`}
|
|
266
|
+
/>
|
|
267
|
+
<div
|
|
268
|
+
style={{
|
|
269
|
+
width: "1.5px",
|
|
270
|
+
flex: 1,
|
|
271
|
+
minHeight: "8px",
|
|
272
|
+
backgroundColor: "rgba(255,255,255,0.35)",
|
|
273
|
+
margin: "2px 0",
|
|
274
|
+
}}
|
|
275
|
+
/>
|
|
276
|
+
<img
|
|
277
|
+
src={serviceItem.icons?.whiteDestination}
|
|
278
|
+
alt="destination"
|
|
279
|
+
className={`w-[15px] h-[15px] shrink-0 ${isSoldOut ? "grayscale" : ""}`}
|
|
280
|
+
style={{ opacity: isSoldOut ? 0.5 : 1 }}
|
|
281
|
+
/>
|
|
282
|
+
</div>
|
|
283
|
+
{/* Text column */}
|
|
284
|
+
<div className="flex flex-col gap-[6px]">
|
|
285
|
+
<div className="flex items-center gap-[6px]">
|
|
286
|
+
<span className="text-[14px] bold-text">
|
|
287
|
+
{cityOrigin?.label.split(",")[0]}
|
|
288
|
+
</span>
|
|
289
|
+
<span className="text-[14px] bold-text">→</span>
|
|
290
|
+
<span className="text-[14px] bold-text">
|
|
291
|
+
{cityDestination?.label.split(",")[0]}
|
|
292
|
+
</span>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div
|
|
296
|
+
className="kupos-time-dd relative"
|
|
297
|
+
tabIndex={0}
|
|
298
|
+
onBlur={(e) => {
|
|
299
|
+
if (
|
|
300
|
+
!e.currentTarget.contains(e.relatedTarget as Node)
|
|
301
|
+
) {
|
|
302
|
+
onTimeDropdownToggle?.(null);
|
|
303
|
+
}
|
|
304
|
+
}}
|
|
305
|
+
style={{ outline: "none", marginTop: "3px" }}
|
|
282
306
|
>
|
|
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)]"
|
|
307
|
+
<button
|
|
308
|
+
type="button"
|
|
309
|
+
onClick={() =>
|
|
310
|
+
onTimeDropdownToggle?.(
|
|
311
|
+
isThisTimeDropdownOpen ? null : serviceItem.id,
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
className="flex cursor-pointer select-none items-center gap-[6px] border-none bg-transparent p-0 bold-text text-[13px] text-[white]"
|
|
315
|
+
>
|
|
316
|
+
<span>{departureRange}</span>
|
|
317
|
+
<img
|
|
318
|
+
src={serviceItem?.icons?.downArrow}
|
|
319
|
+
alt="down arrow"
|
|
320
|
+
className={`kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`}
|
|
299
321
|
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",
|
|
322
|
+
width: "12px",
|
|
323
|
+
height: "8px",
|
|
324
|
+
filter: "brightness(0) invert(1)",
|
|
307
325
|
}}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
326
|
+
/>
|
|
327
|
+
</button>
|
|
328
|
+
{isThisTimeDropdownOpen && (
|
|
329
|
+
<>
|
|
330
|
+
<div
|
|
331
|
+
className="absolute left-0 top-[calc(100%+10px)]"
|
|
332
|
+
style={{
|
|
333
|
+
zIndex: 20,
|
|
334
|
+
backgroundColor: "#fff",
|
|
335
|
+
borderRadius: "14px",
|
|
336
|
+
minWidth: "190px",
|
|
337
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
|
|
338
|
+
overflow: "hidden",
|
|
339
|
+
padding: "6px 0",
|
|
340
|
+
}}
|
|
341
|
+
>
|
|
342
|
+
{availableTimeSlots.map((slot) => {
|
|
343
|
+
const isActive =
|
|
344
|
+
slot.label === selectedTimeSlot ||
|
|
345
|
+
slot === activeSlot;
|
|
346
|
+
// Count services in this slot
|
|
347
|
+
const count = services.filter((s) => {
|
|
348
|
+
const depMin = commonService.timeToMinutes(
|
|
349
|
+
s.departure_time,
|
|
350
|
+
);
|
|
351
|
+
return (
|
|
352
|
+
depMin >= slot.start && depMin < slot.end
|
|
353
|
+
);
|
|
354
|
+
}).length;
|
|
355
|
+
return (
|
|
356
|
+
<button
|
|
357
|
+
key={slot.label}
|
|
358
|
+
type="button"
|
|
359
|
+
onClick={() => {
|
|
360
|
+
onTimeSlotChange?.(slot.label);
|
|
361
|
+
onTimeDropdownToggle?.(null);
|
|
362
|
+
// const slotServices = services.filter((s) => {
|
|
363
|
+
// const depMin = commonService.timeToMinutes(
|
|
364
|
+
// s.departure_time,
|
|
365
|
+
// );
|
|
366
|
+
// return (
|
|
367
|
+
// depMin >= slot.start && depMin < slot.end
|
|
368
|
+
// );
|
|
369
|
+
// });
|
|
370
|
+
// console.log(
|
|
371
|
+
// "Selected slot label:",
|
|
372
|
+
// slot.label,
|
|
373
|
+
// );
|
|
374
|
+
// console.log(
|
|
375
|
+
// "Services in selected slot:",
|
|
376
|
+
// slotServices,
|
|
377
|
+
// );
|
|
378
|
+
}}
|
|
379
|
+
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]"}`}
|
|
380
|
+
>
|
|
381
|
+
<span>{slot.label}</span>
|
|
382
|
+
{count > 0 && (
|
|
383
|
+
<span
|
|
384
|
+
className={`text-[11px] rounded-full px-[6px] py-[2px] font-bold ${
|
|
385
|
+
isActive
|
|
386
|
+
? "bg-[white] text-[#FF5C60]"
|
|
387
|
+
: "bg-[#FF5C60] text-[white]"
|
|
388
|
+
}`}
|
|
389
|
+
>
|
|
390
|
+
{count}
|
|
391
|
+
</span>
|
|
392
|
+
)}
|
|
393
|
+
</button>
|
|
317
394
|
);
|
|
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
|
-
)}
|
|
395
|
+
})}
|
|
396
|
+
</div>
|
|
397
|
+
</>
|
|
398
|
+
)}
|
|
399
|
+
</div>
|
|
364
400
|
</div>
|
|
401
|
+
{/* end text column */}
|
|
365
402
|
</div>
|
|
403
|
+
{/* end flex items-start row */}
|
|
366
404
|
</div>
|
|
405
|
+
{/* end flex-col gap-6 relative */}
|
|
367
406
|
</div>
|
|
407
|
+
{/* end text-[white] */}
|
|
368
408
|
|
|
369
409
|
<div className="border-t border-[#363c48] my-[8px]" />
|
|
370
410
|
|
|
@@ -379,54 +419,62 @@ const FeatureServiceUiMobile = ({
|
|
|
379
419
|
operadores compitiendo <br />
|
|
380
420
|
por tu compra
|
|
381
421
|
</span>
|
|
422
|
+
{/* Operator boxes: single full-width box when 1 operator, else flex row */}
|
|
382
423
|
<div
|
|
383
424
|
className="flex gap-[8px] text-[white]"
|
|
384
|
-
style={{ width: "100%" }}
|
|
425
|
+
style={{ width: "100%", justifyContent: "center" }}
|
|
385
426
|
>
|
|
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";
|
|
427
|
+
{(() => {
|
|
428
|
+
const displayOps =
|
|
429
|
+
servicesInActiveSlot.length > 0
|
|
430
|
+
? servicesInActiveSlot.slice(0, 3).map((s: any) => ({
|
|
431
|
+
logo: s.operator_logo_url,
|
|
432
|
+
name: s.operator_name,
|
|
433
|
+
time: s.departure_time,
|
|
434
|
+
seatsAvailable: `${s.available_seats} disponibles`,
|
|
435
|
+
}))
|
|
436
|
+
: operators;
|
|
437
|
+
const isSingle = displayOps.length === 1;
|
|
438
|
+
return displayOps.map((op, idx) => (
|
|
439
|
+
<div
|
|
440
|
+
key={idx}
|
|
441
|
+
className="flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]"
|
|
442
|
+
style={{
|
|
443
|
+
flex: isSingle ? "none" : 1,
|
|
444
|
+
width: isSingle ? "32%" : undefined,
|
|
445
|
+
margin: isSingle ? "0 auto" : undefined,
|
|
446
|
+
minWidth: 0,
|
|
447
|
+
height: "100px",
|
|
448
|
+
border: "1px solid #363c48",
|
|
449
|
+
backgroundColor: "#1a202e",
|
|
450
|
+
padding: "14px 6px",
|
|
414
451
|
}}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
452
|
+
>
|
|
453
|
+
<img
|
|
454
|
+
src={op.logo}
|
|
455
|
+
alt={op.name}
|
|
456
|
+
onError={(e) => {
|
|
457
|
+
e.currentTarget.src =
|
|
458
|
+
serviceItem?.operator_details?.[0] ||
|
|
459
|
+
"/images/service-list/bus-icon.svg";
|
|
460
|
+
}}
|
|
461
|
+
className={`object-contain ${
|
|
462
|
+
isSoldOut ? "grayscale" : ""
|
|
463
|
+
}`}
|
|
464
|
+
style={{
|
|
465
|
+
height: "24px",
|
|
466
|
+
maxWidth: "100%",
|
|
467
|
+
}}
|
|
468
|
+
/>
|
|
469
|
+
<span className="text-[12px] truncate max-w-full text-center">
|
|
470
|
+
{op.name}
|
|
471
|
+
</span>
|
|
472
|
+
<span className="text-[11px] whitespace-nowrap">
|
|
473
|
+
{op?.seatsAvailable}
|
|
474
|
+
</span>
|
|
475
|
+
</div>
|
|
476
|
+
));
|
|
477
|
+
})()}
|
|
430
478
|
</div>
|
|
431
479
|
|
|
432
480
|
<div
|