kupos-ui-components-lib 9.9.10 → 9.10.0
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/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemDesktop.js +20 -2
- package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemMobile.js +1 -1
- package/dist/components/ServiceItem/mobileTypes.d.ts +2 -0
- package/dist/components/ServiceItem/types.d.ts +7 -0
- package/dist/components/Survey/SurveyDesktop.d.ts +1 -1
- package/dist/components/Survey/SurveyDesktop.js +18 -24
- package/dist/components/Survey/SurveyMobile.d.ts +1 -1
- package/dist/components/Survey/SurveyMobile.js +16 -25
- package/dist/components/Survey/types.d.ts +4 -0
- package/dist/styles.css +3 -0
- package/dist/ui/SeatSection/SeatSection.d.ts +7 -1
- package/dist/ui/SeatSection/SeatSection.js +38 -9
- package/dist/ui/Survey/FeedbackTextarea.d.ts +4 -1
- package/dist/ui/Survey/FeedbackTextarea.js +19 -21
- package/dist/ui/Survey/ScoreButtons.d.ts +5 -1
- package/dist/ui/Survey/ScoreButtons.js +13 -9
- package/dist/ui/Survey/constants.d.ts +1 -1
- package/dist/ui/Survey/constants.js +1 -1
- package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +2 -1
- package/dist/ui/mobileweb/DateTimeSectionMobile.js +12 -6
- package/dist/ui/mobileweb/SeatSectionMobile.d.ts +2 -1
- package/dist/ui/mobileweb/SeatSectionMobile.js +21 -14
- package/dist/utils/CommonService.d.ts +1 -1
- package/dist/utils/CommonService.js +5 -1
- package/package.json +1 -1
- package/src/components/ServiceItem/ServiceItemDesktop.tsx +43 -0
- package/src/components/ServiceItem/ServiceItemMobile.tsx +2 -0
- package/src/components/ServiceItem/mobileTypes.ts +32 -26
- package/src/components/ServiceItem/types.ts +12 -0
- package/src/ui/SeatSection/SeatSection.tsx +86 -18
- package/src/ui/mobileweb/DateTimeSectionMobile.tsx +44 -35
- package/src/ui/mobileweb/SeatSectionMobile.tsx +26 -11
- package/src/utils/CommonService.ts +7 -1
|
@@ -11,7 +11,7 @@ const EXCEPTIONS = [
|
|
|
11
11
|
"blanco",
|
|
12
12
|
"asiento_mascota",
|
|
13
13
|
];
|
|
14
|
-
const SeatRow = ({ type, index, displayLabel, fare, isSoldOut, seatPriceColor, hasMultipleTypes, textSize, }) => {
|
|
14
|
+
const SeatRow = ({ type, index, displayLabel, fare, isSoldOut, seatPriceColor, hasMultipleTypes, textSize, isTrain, }) => {
|
|
15
15
|
if (EXCEPTIONS.includes(type.label))
|
|
16
16
|
return null;
|
|
17
17
|
const rowClass = hasMultipleTypes
|
|
@@ -20,7 +20,9 @@ const SeatRow = ({ type, index, displayLabel, fare, isSoldOut, seatPriceColor, h
|
|
|
20
20
|
const labelColor = isSoldOut ? "#bbb" : "#464647";
|
|
21
21
|
const priceColor = isSoldOut ? "#bbb" : seatPriceColor;
|
|
22
22
|
return (React.createElement("div", { className: rowClass, key: index },
|
|
23
|
-
React.createElement("span", { className: `min-[420]:text-[13px] ${textSize} `, style: { color: labelColor } },
|
|
23
|
+
React.createElement("span", { className: `min-[420]:text-[13px] ${textSize} `, style: { color: labelColor } }, isTrain
|
|
24
|
+
? commonService.truncateSeatLabel(commonService.capitalize(displayLabel), 8)
|
|
25
|
+
: displayLabel),
|
|
24
26
|
React.createElement("span", { className: `min-[420]:text-[13px] ${textSize} bold-text`, style: { color: priceColor } }, fare)));
|
|
25
27
|
};
|
|
26
28
|
const getFilteredSeats = (item) => {
|
|
@@ -42,8 +44,8 @@ const getUniqueSeats = (data, limit) => {
|
|
|
42
44
|
.sort((a, b) => a.fare - b.fare)
|
|
43
45
|
.slice(0, limit);
|
|
44
46
|
};
|
|
45
|
-
function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, }) {
|
|
46
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
47
|
+
function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPriceColor, currencySign, availableSeats, removeDuplicateSeats, serviceItem, tooltipBgColor, showLastSeats, discountSeatPriceColor, isTrain, }) {
|
|
48
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
47
49
|
const hasMultipleTypes = ((_a = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.length) !== null && _a !== void 0 ? _a : 0) > 2;
|
|
48
50
|
const getFare = (fare) => {
|
|
49
51
|
if (removeDuplicateSeats && availableSeats <= 0 && !isPeru) {
|
|
@@ -101,19 +103,24 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
101
103
|
React.createElement("span", { className: "min-[420]:text-[13px] text-[12px] text-[#464647]", style: { opacity: isSoldOut ? 0.5 : 1 } }, "Agotado"))) : null));
|
|
102
104
|
};
|
|
103
105
|
const renderSeats = () => {
|
|
104
|
-
var _a, _b
|
|
106
|
+
var _a, _b;
|
|
105
107
|
if (isPeru) {
|
|
106
108
|
return renderPeruSeats();
|
|
107
109
|
}
|
|
108
110
|
if (removeDuplicateSeats) {
|
|
109
111
|
const uniqueSeats = getUniqueSeats(seatTypesData, 3);
|
|
110
|
-
return uniqueSeats.map((type, i) => (React.createElement(SeatRow, { key: i, type: type, index: i, displayLabel: commonService.truncateSeatLabel(type.label), fare: getFare(type.fare), isSoldOut: isSoldOut, seatPriceColor: seatPriceColor, hasMultipleTypes: hasMultipleTypes, textSize: "text-[11px]" })));
|
|
112
|
+
return uniqueSeats.map((type, i) => (React.createElement(SeatRow, { key: i, type: type, index: i, displayLabel: commonService.truncateSeatLabel(type.label), fare: getFare(type.fare), isSoldOut: isSoldOut, seatPriceColor: seatPriceColor, hasMultipleTypes: hasMultipleTypes, textSize: "text-[11px]", isTrain: isTrain })));
|
|
111
113
|
}
|
|
112
|
-
|
|
114
|
+
const filteredSeats = (_a = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label))) === null || _a === void 0 ? void 0 : _a.sort((a, b) => a.fare - b.fare);
|
|
115
|
+
return (_b = (isTrain ? filteredSeats : filteredSeats === null || filteredSeats === void 0 ? void 0 : filteredSeats.slice(0, 2))) === null || _b === void 0 ? void 0 : _b.map((type, i) => (React.createElement(SeatRow, { key: i, type: type, index: i, displayLabel: type.label, fare: getFare(type.fare), isSoldOut: isSoldOut, seatPriceColor: seatPriceColor, hasMultipleTypes: hasMultipleTypes, textSize: "text-[12px]", isTrain: isTrain })));
|
|
113
116
|
};
|
|
114
117
|
const seats = removeDuplicateSeats
|
|
115
118
|
? getUniqueSeats(seatTypesData, 3)
|
|
116
|
-
: (
|
|
119
|
+
: (() => {
|
|
120
|
+
var _a;
|
|
121
|
+
const filtered = (_a = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label))) === null || _a === void 0 ? void 0 : _a.sort((a, b) => a.fare - b.fare);
|
|
122
|
+
return isTrain ? filtered : filtered === null || filtered === void 0 ? void 0 : filtered.slice(0, 2);
|
|
123
|
+
})();
|
|
117
124
|
const discountedSeats = seats === null || seats === void 0 ? void 0 : seats.map((seat) => (Object.assign(Object.assign({}, seat), commonService.calculateDiscountedPrice(seat.fare, serviceItem))));
|
|
118
125
|
const peruLowestFare = isPeru ? getLowestFare() : null;
|
|
119
126
|
const peruDiscountCalc = isPeru && peruLowestFare != null
|
|
@@ -124,7 +131,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
124
131
|
peruDiscountCalc.originalPrice !== peruDiscountCalc.discountedPrice
|
|
125
132
|
: discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.some((s) => s.originalPrice !== s.discountedPrice);
|
|
126
133
|
const discountSeat = isPeru && peruDiscountCalc
|
|
127
|
-
? Object.assign({ label: "", fare: peruLowestFare }, peruDiscountCalc) : (
|
|
134
|
+
? Object.assign({ label: "", fare: peruLowestFare }, peruDiscountCalc) : (_b = discountedSeats === null || discountedSeats === void 0 ? void 0 : discountedSeats.filter((seat) => !EXCEPTIONS.includes(seat.label))) === null || _b === void 0 ? void 0 : _b.sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
|
|
128
135
|
const discountValue = (() => {
|
|
129
136
|
if ((serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_type) === "percentage" &&
|
|
130
137
|
typeof (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.discount_value) === "number") {
|
|
@@ -147,14 +154,14 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
147
154
|
};
|
|
148
155
|
const originalDpPrice = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.original_dp_price);
|
|
149
156
|
const dpDiscountPercent = getMinValue(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents);
|
|
150
|
-
const firstSeatFare = (
|
|
157
|
+
const firstSeatFare = (_d = (_c = seatTypesData === null || seatTypesData === void 0 ? void 0 : seatTypesData.filter((item) => getFilteredSeats(item.label) && !EXCEPTIONS.includes(item.label))) === null || _c === void 0 ? void 0 : _c.sort((a, b) => a.fare - b.fare)[0]) === null || _d === void 0 ? void 0 : _d.fare;
|
|
151
158
|
const hasDpDiscount = (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) &&
|
|
152
159
|
(serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discount_percents) &&
|
|
153
160
|
originalDpPrice != null &&
|
|
154
161
|
dpDiscountPercent != null &&
|
|
155
162
|
firstSeatFare != null;
|
|
156
163
|
return (React.createElement("div", { className: "content-center relative", style: { width: "40%" } }, (serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.is_dp_enabled) &&
|
|
157
|
-
!((
|
|
164
|
+
!((_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.dp_discounted_seats) === null || _e === void 0 ? void 0 : _e.length) &&
|
|
158
165
|
!dpDiscountPercent ? (React.createElement("div", { className: "flex flex-col justify-between h-[2.5rem]", style: { gap: isSoldOut ? "0px" : "5px" } }, renderDpSeats())) : hasDpDiscount ? (React.createElement("div", { className: "relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] " },
|
|
159
166
|
!isNaN(Number(dpDiscountPercent)) &&
|
|
160
167
|
Number(dpDiscountPercent) > 0 && (React.createElement("div", { className: "absolute -top-[18px] right-[0px]", style: {
|
|
@@ -186,7 +193,7 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
186
193
|
React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
|
|
187
194
|
color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
|
|
188
195
|
} },
|
|
189
|
-
((
|
|
196
|
+
((_f = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _f === void 0 ? void 0 : _f.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[16px] w-[16px] object-contain", style: { filter: isSoldOut ? "grayscale" : "" } })) : null,
|
|
190
197
|
commonService.discountedCurrency(Number(firstSeatFare), currencySign)),
|
|
191
198
|
isSoldOut ? (React.createElement("span", { className: "col-span-2 min-[420]:text-[13px] text-right text-[12px] text-[#ccc]" }, "Agotado")) : null)) : hasDiscount && discountSeat ? (React.createElement("div", null,
|
|
192
199
|
React.createElement("div", { className: "relative grid grid-cols-[auto_auto] justify-between gap-x-[8px] " },
|
|
@@ -220,8 +227,8 @@ function SeatSectionMobile({ seatTypes: seatTypesData, isSoldOut, isPeru, seatPr
|
|
|
220
227
|
React.createElement("span", { className: "flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]", style: {
|
|
221
228
|
color: isSoldOut ? "#bbb" : discountSeatPriceColor || "#ff5964",
|
|
222
229
|
} },
|
|
223
|
-
((
|
|
224
|
-
commonService.discountedCurrency(discountSeat.discountedPrice, currencySign))))) : (React.createElement("div", { className:
|
|
230
|
+
((_g = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _g === void 0 ? void 0 : _g.fireIcon) ? (React.createElement("img", { src: serviceItem.icons.fireIcon, alt: "discount", className: "h-[16px] w-[16px] object-contain", style: { opacity: isSoldOut ? 0.5 : 1 } })) : null,
|
|
231
|
+
commonService.discountedCurrency(discountSeat.discountedPrice, currencySign))))) : (React.createElement("div", { className: `flex flex-col justify-between ${isTrain ? "" : "h-[2.5rem]"} `, style: {
|
|
225
232
|
gap: isSoldOut ? "0px" : "5px",
|
|
226
233
|
justifyContent: hasMultipleTypes ? "space-between" : "center",
|
|
227
234
|
} },
|
|
@@ -3,7 +3,7 @@ declare const commonService: {
|
|
|
3
3
|
discountedCurrency(amount: number, currencySign?: string): string;
|
|
4
4
|
copyObject: (ob: any) => any;
|
|
5
5
|
getServiceTypeLabelForFilters: (service_type: any) => "Tipo de servicio" | "Punto de embarque" | "Tipo de asiento" | "SERVICIOS" | "";
|
|
6
|
-
truncateSeatLabel: (label: string | number) => string;
|
|
6
|
+
truncateSeatLabel: (label: string | number, maxLength?: number) => string;
|
|
7
7
|
getAmenitiesImage: (name: string, serviceItem: any) => string;
|
|
8
8
|
getAmenityName: (rawAmenity: string) => string;
|
|
9
9
|
getSeatNameForFilters: (rawSeat: any) => any;
|
|
@@ -34,11 +34,15 @@ const commonService = {
|
|
|
34
34
|
return "";
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
truncateSeatLabel: (label) => {
|
|
37
|
+
truncateSeatLabel: (label, maxLength) => {
|
|
38
38
|
if (typeof label !== "string")
|
|
39
39
|
return String(label);
|
|
40
40
|
if (label.includes("("))
|
|
41
41
|
return label;
|
|
42
|
+
// If maxLength provided, hard-truncate regardless of word count
|
|
43
|
+
if (maxLength != null && label.length > maxLength) {
|
|
44
|
+
return label.slice(0, maxLength) + "...";
|
|
45
|
+
}
|
|
42
46
|
const words = label.trim().split(/\s+/);
|
|
43
47
|
const truncateWord = (word) => word.length > 5 ? word.slice(0, 3) + "..." : word;
|
|
44
48
|
if (words.length === 1)
|
package/package.json
CHANGED
|
@@ -131,6 +131,13 @@ function ServiceItemPB({
|
|
|
131
131
|
showLoginModal,
|
|
132
132
|
isLoggedIn,
|
|
133
133
|
showLoginOption,
|
|
134
|
+
isTrain,
|
|
135
|
+
selectedSeatKey,
|
|
136
|
+
onSeatSelect,
|
|
137
|
+
onTrainButtonClick,
|
|
138
|
+
showSeatSelectionError,
|
|
139
|
+
onShowSeatSelectionError,
|
|
140
|
+
onClearSeatSelectionError,
|
|
134
141
|
isFeatureDropDownExpand,
|
|
135
142
|
setIsFeatureDropDownExpand,
|
|
136
143
|
ticketQuantity,
|
|
@@ -144,6 +151,15 @@ function ServiceItemPB({
|
|
|
144
151
|
wowDealData,
|
|
145
152
|
isFlores,
|
|
146
153
|
}: ServiceItemProps & { currencySign?: string }): React.ReactElement {
|
|
154
|
+
const handleSeatSelect = (
|
|
155
|
+
key: any,
|
|
156
|
+
price: number,
|
|
157
|
+
seatKey: string,
|
|
158
|
+
apiSeatType?: string,
|
|
159
|
+
) => {
|
|
160
|
+
onClearSeatSelectionError?.();
|
|
161
|
+
onSeatSelect?.(key, price, seatKey, apiSeatType);
|
|
162
|
+
};
|
|
147
163
|
const getAnimationIcon = (icon: string) => {
|
|
148
164
|
const animation = ANIMATION_MAP[icon];
|
|
149
165
|
if (!animation) return null;
|
|
@@ -344,6 +360,17 @@ function ServiceItemPB({
|
|
|
344
360
|
return;
|
|
345
361
|
}
|
|
346
362
|
|
|
363
|
+
if (isTrain) {
|
|
364
|
+
if (!selectedSeatKey) {
|
|
365
|
+
onShowSeatSelectionError?.(serviceItem.id);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (onTrainButtonClick) {
|
|
369
|
+
onTrainButtonClick();
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
347
374
|
onBookButtonPress();
|
|
348
375
|
};
|
|
349
376
|
|
|
@@ -597,6 +624,10 @@ function ServiceItemPB({
|
|
|
597
624
|
isPeru={isPeru}
|
|
598
625
|
renderIcon={renderIcon}
|
|
599
626
|
discountSeatPriceColor={colors.discountSeatPriceColor}
|
|
627
|
+
isTrain={isTrain}
|
|
628
|
+
selectedSeatKey={selectedSeatKey}
|
|
629
|
+
onSeatSelect={handleSeatSelect}
|
|
630
|
+
topLabelColor={colors.topLabelColor}
|
|
600
631
|
/>
|
|
601
632
|
</div>
|
|
602
633
|
|
|
@@ -612,6 +643,18 @@ function ServiceItemPB({
|
|
|
612
643
|
soldOutIcon={renderIcon("soldOutIcon", "14px")}
|
|
613
644
|
onClick={checkMidnight}
|
|
614
645
|
/>
|
|
646
|
+
{showSeatSelectionError === serviceItem.id && isTrain && (
|
|
647
|
+
<div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
|
|
648
|
+
<div
|
|
649
|
+
className="text-[9px] text-center whitespace-nowrap"
|
|
650
|
+
style={{
|
|
651
|
+
color: colors.seatPriceColor,
|
|
652
|
+
}}
|
|
653
|
+
>
|
|
654
|
+
Selecciona el tipo de servicio
|
|
655
|
+
</div>
|
|
656
|
+
</div>
|
|
657
|
+
)}
|
|
615
658
|
{showLastSeats ? (
|
|
616
659
|
<div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
|
|
617
660
|
{serviceItem?.available_seats < 10 &&
|
|
@@ -135,36 +135,36 @@ export interface MobileServiceItemProps {
|
|
|
135
135
|
bombAnim?: string;
|
|
136
136
|
whiteBoardingIcon?: string;
|
|
137
137
|
downArrow?: string;
|
|
138
|
-
personIcon?: string
|
|
138
|
+
personIcon?: string;
|
|
139
139
|
specialDeparture?: string;
|
|
140
140
|
fireIcon?: string;
|
|
141
141
|
directoIcon?: string;
|
|
142
|
-
whiteFireIcon?: string
|
|
143
|
-
femaleAnim?:string
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
whiteDestination?: string
|
|
148
|
-
userIcon?: string
|
|
149
|
-
|
|
150
|
-
sheildIcon?: string
|
|
151
|
-
busIcon?: string
|
|
152
|
-
whiteDownArrow?: string
|
|
153
|
-
empressaIcon?: string
|
|
154
|
-
flexibleIcon?: string
|
|
155
|
-
listoIcon?: string
|
|
156
|
-
precioIcon?: string
|
|
157
|
-
confirmarIcon?: string
|
|
142
|
+
whiteFireIcon?: string;
|
|
143
|
+
femaleAnim?: string;
|
|
144
|
+
thunderAnim?: string;
|
|
145
|
+
personsAnim?: string;
|
|
146
|
+
whiteOrigin?: string;
|
|
147
|
+
whiteDestination?: string;
|
|
148
|
+
userIcon?: string;
|
|
149
|
+
|
|
150
|
+
sheildIcon?: string;
|
|
151
|
+
busIcon?: string;
|
|
152
|
+
whiteDownArrow?: string;
|
|
153
|
+
empressaIcon?: string;
|
|
154
|
+
flexibleIcon?: string;
|
|
155
|
+
listoIcon?: string;
|
|
156
|
+
precioIcon?: string;
|
|
157
|
+
confirmarIcon?: string;
|
|
158
158
|
cancelTicketIcon?: string;
|
|
159
159
|
changeTicketIcon?: string;
|
|
160
160
|
petFriendlyIcon?: string;
|
|
161
|
-
womenSeatIcon?: string
|
|
161
|
+
womenSeatIcon?: string;
|
|
162
162
|
[key: string]: string | Record<string, string | undefined> | undefined;
|
|
163
163
|
};
|
|
164
164
|
useLottieFor?: string[];
|
|
165
165
|
};
|
|
166
166
|
onBookButtonPress?: () => void;
|
|
167
|
-
onRemateUiButtonClick?: ()=> void;
|
|
167
|
+
onRemateUiButtonClick?: () => void;
|
|
168
168
|
terminals?: any[];
|
|
169
169
|
showDropdown?: boolean;
|
|
170
170
|
setShowDropdown?: (value: boolean) => void;
|
|
@@ -208,7 +208,7 @@ export interface MobileServiceItemProps {
|
|
|
208
208
|
seatPriceColor?: string;
|
|
209
209
|
rightGradiantColor?: string;
|
|
210
210
|
leftGradiantColor?: string;
|
|
211
|
-
discountSeatPriceColor?: string
|
|
211
|
+
discountSeatPriceColor?: string;
|
|
212
212
|
};
|
|
213
213
|
isCiva?: boolean;
|
|
214
214
|
currencySign?: string;
|
|
@@ -221,23 +221,29 @@ export interface MobileServiceItemProps {
|
|
|
221
221
|
showLastSeats?: boolean;
|
|
222
222
|
removeDuplicateSeats?: boolean;
|
|
223
223
|
isLinatal?: boolean;
|
|
224
|
-
|
|
224
|
+
viewersConfig?: {
|
|
225
225
|
min: number;
|
|
226
226
|
max: number;
|
|
227
227
|
interval?: number; // ms, default 5000
|
|
228
228
|
label?: string; // e.g. "personas están viendo este viaje"
|
|
229
229
|
icon?: string; // optional icon URL
|
|
230
230
|
};
|
|
231
|
-
|
|
231
|
+
operatorLabel?: string;
|
|
232
|
+
isTrain?: boolean;
|
|
233
|
+
isFeatureDropDownExpand?: any;
|
|
232
234
|
setIsFeatureDropDownExpand?: (value: any) => void;
|
|
233
235
|
ticketQuantity?: number;
|
|
234
|
-
onIncreaseTicketQuantity?: (
|
|
235
|
-
|
|
236
|
+
onIncreaseTicketQuantity?: (
|
|
237
|
+
serviceItem: MobileServiceItemProps["serviceItem"],
|
|
238
|
+
) => void;
|
|
239
|
+
onDecreaseTicketQuantity?: (
|
|
240
|
+
serviceItem: MobileServiceItemProps["serviceItem"],
|
|
241
|
+
) => void;
|
|
236
242
|
cityOrigin?: { value: number; label: string };
|
|
237
243
|
cityDestination?: { value: number; label: string };
|
|
238
|
-
|
|
244
|
+
isNewUi?: boolean;
|
|
239
245
|
|
|
240
|
-
|
|
246
|
+
selectedTimeSlot?: string;
|
|
241
247
|
onTimeSlotChange?: (slot: string) => void;
|
|
242
248
|
isTimeDropdownOpen?: string | number | null;
|
|
243
249
|
onTimeDropdownToggle?: (id?: string | number | null) => void;
|
|
@@ -252,6 +252,18 @@ export interface ServiceItemProps {
|
|
|
252
252
|
showLoginModal?: any;
|
|
253
253
|
isLoggedIn?: any;
|
|
254
254
|
showLoginOption?: boolean;
|
|
255
|
+
isTrain?: boolean;
|
|
256
|
+
selectedSeatKey?: any;
|
|
257
|
+
onSeatSelect?: (
|
|
258
|
+
key: any,
|
|
259
|
+
price: number,
|
|
260
|
+
seatKey: string,
|
|
261
|
+
apiSeatType?: string,
|
|
262
|
+
) => void;
|
|
263
|
+
onTrainButtonClick?: any;
|
|
264
|
+
showSeatSelectionError?: string | null;
|
|
265
|
+
onShowSeatSelectionError?: (serviceId: string) => void;
|
|
266
|
+
onClearSeatSelectionError?: () => void;
|
|
255
267
|
selectedTimeSlot?: string;
|
|
256
268
|
onTimeSlotChange?: (slot: string) => void;
|
|
257
269
|
isTimeDropdownOpen?: string | number | null;
|
|
@@ -7,6 +7,8 @@ interface SeatType {
|
|
|
7
7
|
label: string;
|
|
8
8
|
fare: number;
|
|
9
9
|
key: any;
|
|
10
|
+
apiSeatType?: string;
|
|
11
|
+
api_seat_type?: string;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
interface SeatSectionProps {
|
|
@@ -21,6 +23,15 @@ interface SeatSectionProps {
|
|
|
21
23
|
serviceItem?: any;
|
|
22
24
|
renderIcon?: (iconKey: string, size?: string) => React.ReactNode;
|
|
23
25
|
discountSeatPriceColor?: string;
|
|
26
|
+
isTrain?: boolean;
|
|
27
|
+
selectedSeatKey?: any;
|
|
28
|
+
onSeatSelect?: (
|
|
29
|
+
key: any,
|
|
30
|
+
price: number,
|
|
31
|
+
seatKey: string,
|
|
32
|
+
apiSeatType?: string,
|
|
33
|
+
) => void;
|
|
34
|
+
topLabelColor?: string;
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
@@ -31,6 +42,8 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
|
31
42
|
let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
|
|
32
43
|
label: val?.label,
|
|
33
44
|
price: val?.fare,
|
|
45
|
+
key: val?.key,
|
|
46
|
+
apiSeatType: val?.apiSeatType || val?.api_seat_type,
|
|
34
47
|
}));
|
|
35
48
|
|
|
36
49
|
seatTypesWithPrices.sort((a, b) => a.price - b.price);
|
|
@@ -38,7 +51,7 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
|
38
51
|
return seatTypesWithPrices;
|
|
39
52
|
}
|
|
40
53
|
|
|
41
|
-
function getSortedSeatTypes(seatTypes: SeatType[]) {
|
|
54
|
+
function getSortedSeatTypes(seatTypes: SeatType[], isTrain: any) {
|
|
42
55
|
if (!seatTypes?.length) {
|
|
43
56
|
return [{ label: "Salon cama", price: 0 }];
|
|
44
57
|
}
|
|
@@ -52,7 +65,9 @@ function getSortedSeatTypes(seatTypes: SeatType[]) {
|
|
|
52
65
|
seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
|
|
68
|
+
if (!isTrain) {
|
|
69
|
+
seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
|
|
70
|
+
}
|
|
56
71
|
|
|
57
72
|
const seenPrices = new Set<number>();
|
|
58
73
|
seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
|
|
@@ -97,14 +112,18 @@ function SeatSection({
|
|
|
97
112
|
priceColor,
|
|
98
113
|
currencySign,
|
|
99
114
|
removeDuplicateSeats,
|
|
115
|
+
selectedSeatKey,
|
|
116
|
+
onSeatSelect,
|
|
100
117
|
isPeru,
|
|
101
118
|
serviceItem,
|
|
102
119
|
renderIcon,
|
|
103
120
|
dpSeatColor,
|
|
104
121
|
discountSeatPriceColor,
|
|
122
|
+
isTrain,
|
|
123
|
+
topLabelColor,
|
|
105
124
|
}: SeatSectionProps): React.ReactElement {
|
|
106
125
|
const uniqueSeats = getUniqueSeats(seatTypes);
|
|
107
|
-
const sortedSeatTypes = getSortedSeatTypes(seatTypes);
|
|
126
|
+
const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
|
|
108
127
|
const numberOfSeats = getNumberOfSeats(seatTypes);
|
|
109
128
|
const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
|
|
110
129
|
|
|
@@ -116,22 +135,71 @@ function SeatSection({
|
|
|
116
135
|
const renderSeatNames = () => {
|
|
117
136
|
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
118
137
|
|
|
119
|
-
return seats.map((val, key: number) =>
|
|
120
|
-
SEAT_EXCEPTIONS.includes(val.label) ? null : (
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
138
|
+
return seats.map((val, key: number) => {
|
|
139
|
+
return SEAT_EXCEPTIONS.includes(val.label) ? null : (
|
|
140
|
+
<div
|
|
141
|
+
className="flex items-center"
|
|
142
|
+
style={isTrain ? { cursor: "pointer" } : undefined}
|
|
143
|
+
onClick={
|
|
144
|
+
isTrain && !isSoldOut
|
|
145
|
+
? () =>
|
|
146
|
+
val.label === selectedSeatKey
|
|
147
|
+
? onSeatSelect?.(null, 0, "", "")
|
|
148
|
+
: onSeatSelect?.(
|
|
149
|
+
val.label,
|
|
150
|
+
val.price,
|
|
151
|
+
val.key,
|
|
152
|
+
(val as any).apiSeatType,
|
|
153
|
+
)
|
|
154
|
+
: undefined
|
|
155
|
+
}
|
|
126
156
|
>
|
|
127
|
-
{
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
{isTrain && (
|
|
158
|
+
<div
|
|
159
|
+
style={{
|
|
160
|
+
border: `1px solid ${val.label === selectedSeatKey ? topLabelColor : "#ccc"}`,
|
|
161
|
+
borderRadius: "50%",
|
|
162
|
+
width: "14px",
|
|
163
|
+
height: "14px",
|
|
164
|
+
minWidth: "14px",
|
|
165
|
+
marginRight: "10px",
|
|
166
|
+
display: "flex",
|
|
167
|
+
alignItems: "center",
|
|
168
|
+
justifyContent: "center",
|
|
169
|
+
}}
|
|
170
|
+
>
|
|
171
|
+
{val.label === selectedSeatKey && (
|
|
172
|
+
<div
|
|
173
|
+
style={{
|
|
174
|
+
backgroundColor: topLabelColor,
|
|
175
|
+
borderRadius: "50%",
|
|
176
|
+
width: "7px",
|
|
177
|
+
height: "7px",
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
<span
|
|
184
|
+
key={key}
|
|
185
|
+
className={`flex items-center justify-between text-[13.33px] ${
|
|
186
|
+
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
187
|
+
}`}
|
|
188
|
+
>
|
|
189
|
+
{typeof val.label === "string" || typeof val.label === "number"
|
|
190
|
+
? removeDuplicateSeats && isPeru
|
|
191
|
+
? CommonService.truncateSeatLabel(val.label)
|
|
192
|
+
: isTrain
|
|
193
|
+
? CommonService.truncateSeatLabel(
|
|
194
|
+
CommonService.capitalize(String(val.label)),
|
|
195
|
+
8,
|
|
196
|
+
)
|
|
197
|
+
: val.label
|
|
198
|
+
: null}
|
|
199
|
+
</span>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
});
|
|
135
203
|
};
|
|
136
204
|
|
|
137
205
|
const renderSeatPrices = () => {
|
|
@@ -25,6 +25,7 @@ interface DateTimeSectionMobileProps {
|
|
|
25
25
|
tooltipBgColor?: string;
|
|
26
26
|
showLastSeats?: boolean;
|
|
27
27
|
discountSeatPriceColor?: string;
|
|
28
|
+
isTrain?: boolean;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const pad = (n: number) => (n < 10 ? "0" + n : String(n));
|
|
@@ -67,41 +68,43 @@ const TimeRow: React.FC<TimeRowProps> = ({
|
|
|
67
68
|
isSoldOut,
|
|
68
69
|
}) => {
|
|
69
70
|
const formattedDate = DateService.getServiceItemDate(date);
|
|
70
|
-
const dotPositionClass = formattedDate.includes("dom")
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
71
|
+
const dotPositionClass = formattedDate.includes("dom")
|
|
72
|
+
? "max-[399px]:left-[53%]"
|
|
73
|
+
: "";
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
className={`flex items-center min-[420]:text-[13px] text-[12px] justify-between ${
|
|
77
|
+
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
78
|
+
}`}
|
|
79
|
+
>
|
|
80
|
+
<div className="flex items-center" style={{ flex: 1 }}>
|
|
81
|
+
<div>
|
|
82
|
+
{" "}
|
|
83
|
+
{label ? (
|
|
84
|
+
<div className="w-[60px]">{label}</div>
|
|
85
|
+
) : (
|
|
86
|
+
<div className="w-[12px] h-auto mr-[5px]">
|
|
87
|
+
<img
|
|
88
|
+
src={icon}
|
|
89
|
+
alt={alt}
|
|
90
|
+
className={`w-[12px] h-auto mr-[5px] ${
|
|
91
|
+
isSoldOut ? "grayscale" : ""
|
|
92
|
+
}`}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
<div
|
|
98
|
+
className="flex items-center relative capitalize justify-between"
|
|
99
|
+
style={{ flex: 1 }}
|
|
100
|
+
>
|
|
101
|
+
<span className="cursor-pointer black-text">{formattedDate}</span>
|
|
102
|
+
<div className={`absolute left-[50%] ${dotPositionClass}`}>•</div>
|
|
103
|
+
<div className="font-[900] relative black-text">{timeContent}</div>
|
|
104
|
+
</div>
|
|
102
105
|
</div>
|
|
103
106
|
</div>
|
|
104
|
-
|
|
107
|
+
);
|
|
105
108
|
};
|
|
106
109
|
|
|
107
110
|
function DateTimeSectionMobile({
|
|
@@ -127,6 +130,7 @@ function DateTimeSectionMobile({
|
|
|
127
130
|
tooltipBgColor,
|
|
128
131
|
showLastSeats,
|
|
129
132
|
discountSeatPriceColor,
|
|
133
|
+
isTrain,
|
|
130
134
|
}: DateTimeSectionMobileProps): React.ReactElement {
|
|
131
135
|
const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
|
|
132
136
|
|
|
@@ -153,8 +157,12 @@ function DateTimeSectionMobile({
|
|
|
153
157
|
>
|
|
154
158
|
{/* DATE AND TIME */}
|
|
155
159
|
<div
|
|
156
|
-
className=
|
|
157
|
-
style={{
|
|
160
|
+
className={`flex flex-col gap-[4px] w-[50%] ${isTrain ? "justify-center" : "justify-between"}`}
|
|
161
|
+
style={{
|
|
162
|
+
justifyContent: isCiva && "center",
|
|
163
|
+
minHeight: isTrain ? undefined : "2.5rem",
|
|
164
|
+
alignSelf: isTrain ? "stretch" : undefined,
|
|
165
|
+
}}
|
|
158
166
|
>
|
|
159
167
|
<TimeRow
|
|
160
168
|
label={orignLabel}
|
|
@@ -198,6 +206,7 @@ function DateTimeSectionMobile({
|
|
|
198
206
|
tooltipBgColor={tooltipBgColor}
|
|
199
207
|
showLastSeats={showLastSeats}
|
|
200
208
|
discountSeatPriceColor={discountSeatPriceColor}
|
|
209
|
+
isTrain={isTrain}
|
|
201
210
|
/>
|
|
202
211
|
</div>
|
|
203
212
|
);
|