kupos-ui-components-lib 9.11.1 → 9.11.3
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 +41 -5
- package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemMobile.js +1 -1
- package/dist/components/ServiceItem/mobileTypes.d.ts +1 -0
- package/dist/components/ServiceItem/types.d.ts +7 -0
- package/dist/styles.css +6 -0
- package/dist/ui/DateTimeSection/DateTimeSection.js +4 -4
- package/dist/ui/SeatSection/SeatSection.d.ts +7 -1
- package/dist/ui/SeatSection/SeatSection.js +59 -14
- package/dist/ui/mobileweb/DateTimeSectionMobile.d.ts +2 -1
- package/dist/ui/mobileweb/DateTimeSectionMobile.js +13 -7
- package/dist/ui/mobileweb/SeatSectionMobile.d.ts +2 -1
- package/dist/ui/mobileweb/SeatSectionMobile.js +36 -16
- 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 +69 -3
- package/src/components/ServiceItem/ServiceItemMobile.tsx +2 -1
- package/src/components/ServiceItem/mobileTypes.ts +31 -26
- package/src/components/ServiceItem/types.ts +12 -0
- package/src/ui/DateTimeSection/DateTimeSection.tsx +4 -4
- package/src/ui/SeatSection/SeatSection.tsx +114 -24
- package/src/ui/mobileweb/DateTimeSectionMobile.tsx +47 -34
- package/src/ui/mobileweb/SeatSectionMobile.tsx +45 -13
- package/src/utils/CommonService.ts +7 -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,
|
|
@@ -145,7 +152,15 @@ function ServiceItemPB({
|
|
|
145
152
|
isFlores,
|
|
146
153
|
operatorLabel,
|
|
147
154
|
}: ServiceItemProps & { currencySign?: string }): React.ReactElement {
|
|
148
|
-
|
|
155
|
+
const handleSeatSelect = (
|
|
156
|
+
key: any,
|
|
157
|
+
price: number,
|
|
158
|
+
seatKey: string,
|
|
159
|
+
apiSeatType?: string,
|
|
160
|
+
) => {
|
|
161
|
+
onClearSeatSelectionError?.();
|
|
162
|
+
onSeatSelect?.(key, price, seatKey, apiSeatType);
|
|
163
|
+
};
|
|
149
164
|
const getAnimationIcon = (icon: string) => {
|
|
150
165
|
const animation = ANIMATION_MAP[icon];
|
|
151
166
|
if (!animation) return null;
|
|
@@ -225,6 +240,27 @@ function ServiceItemPB({
|
|
|
225
240
|
(seat) => seat.originalPrice !== seat.discountedPrice,
|
|
226
241
|
);
|
|
227
242
|
|
|
243
|
+
// Mirror the same check as SeatSection: hide badge (and its top margin) when
|
|
244
|
+
// both percentage and max_discount exist and the cap is being applied.
|
|
245
|
+
const isMaxDiscountApplied = (() => {
|
|
246
|
+
const { discount_type, discount_value, max_discount } = serviceItem as any;
|
|
247
|
+
if (
|
|
248
|
+
discount_type === "percentage" &&
|
|
249
|
+
typeof discount_value === "number" &&
|
|
250
|
+
max_discount != null &&
|
|
251
|
+
max_discount > 0
|
|
252
|
+
) {
|
|
253
|
+
const lowestFare = discountedSeats
|
|
254
|
+
.map((s) => s.originalPrice)
|
|
255
|
+
.filter((p) => p > 0)
|
|
256
|
+
.sort((a, b) => a - b)[0];
|
|
257
|
+
if (lowestFare != null) {
|
|
258
|
+
return (lowestFare * discount_value) / 100 > max_discount;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
})();
|
|
263
|
+
|
|
228
264
|
const dpDiscountEntry = Object.entries(
|
|
229
265
|
serviceItem?.dp_discount_percents || {},
|
|
230
266
|
)[0];
|
|
@@ -346,6 +382,17 @@ function ServiceItemPB({
|
|
|
346
382
|
return;
|
|
347
383
|
}
|
|
348
384
|
|
|
385
|
+
if (isTrain) {
|
|
386
|
+
if (!selectedSeatKey) {
|
|
387
|
+
onShowSeatSelectionError?.(serviceItem.id);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (onTrainButtonClick) {
|
|
391
|
+
onTrainButtonClick();
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
349
396
|
onBookButtonPress();
|
|
350
397
|
};
|
|
351
398
|
|
|
@@ -532,12 +579,15 @@ function ServiceItemPB({
|
|
|
532
579
|
: "20px 15px 10px 15px",
|
|
533
580
|
|
|
534
581
|
marginTop:
|
|
535
|
-
hasDiscount || hasOfferText || dpDiscountPercent
|
|
582
|
+
(hasDiscount || hasOfferText || dpDiscountPercent) &&
|
|
583
|
+
!isMaxDiscountApplied
|
|
536
584
|
? "14px"
|
|
537
585
|
: "",
|
|
538
586
|
}}
|
|
539
587
|
>
|
|
540
|
-
<div
|
|
588
|
+
<div
|
|
589
|
+
className={`grid text-[#464647] w-full ${isTrain ? "[grid-template-columns:16%_30%_2.5%_28%_15.5%]" : "[grid-template-columns:20%_30%_2.5%_24%_15.5%]"} gap-x-[2%] items-center`}
|
|
590
|
+
>
|
|
541
591
|
{/* OPERATOR LOGO */}
|
|
542
592
|
<div className="flex flex-col gap-[5px]">
|
|
543
593
|
<div>
|
|
@@ -599,6 +649,10 @@ function ServiceItemPB({
|
|
|
599
649
|
isPeru={isPeru}
|
|
600
650
|
renderIcon={renderIcon}
|
|
601
651
|
discountSeatPriceColor={colors.discountSeatPriceColor}
|
|
652
|
+
isTrain={isTrain}
|
|
653
|
+
selectedSeatKey={selectedSeatKey}
|
|
654
|
+
onSeatSelect={handleSeatSelect}
|
|
655
|
+
topLabelColor={colors.topLabelColor}
|
|
602
656
|
tooltipColor={colors.tooltipColor}
|
|
603
657
|
/>
|
|
604
658
|
</div>
|
|
@@ -615,6 +669,18 @@ function ServiceItemPB({
|
|
|
615
669
|
soldOutIcon={renderIcon("soldOutIcon", "14px")}
|
|
616
670
|
onClick={checkMidnight}
|
|
617
671
|
/>
|
|
672
|
+
{showSeatSelectionError === serviceItem.id && isTrain && (
|
|
673
|
+
<div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
|
|
674
|
+
<div
|
|
675
|
+
className="text-[9px] text-center whitespace-nowrap"
|
|
676
|
+
style={{
|
|
677
|
+
color: colors.seatPriceColor,
|
|
678
|
+
}}
|
|
679
|
+
>
|
|
680
|
+
Selecciona el tipo de servicio
|
|
681
|
+
</div>
|
|
682
|
+
</div>
|
|
683
|
+
)}
|
|
618
684
|
{showLastSeats ? (
|
|
619
685
|
<div className="flex justify-center mr-[11px] w-[100%] right-[0px] absolute left-[0] top-[40px]">
|
|
620
686
|
{serviceItem?.available_seats < 10 &&
|
|
@@ -46,6 +46,8 @@ function ServiceItemMobile({
|
|
|
46
46
|
removeDuplicateSeats,
|
|
47
47
|
isLinatal,
|
|
48
48
|
viewersConfig,
|
|
49
|
+
operatorLabel,
|
|
50
|
+
isTrain,
|
|
49
51
|
isFeatureDropDownExpand,
|
|
50
52
|
setIsFeatureDropDownExpand,
|
|
51
53
|
ticketQuantity,
|
|
@@ -61,7 +63,6 @@ function ServiceItemMobile({
|
|
|
61
63
|
onTimeDropdownToggle,
|
|
62
64
|
wowDealData,
|
|
63
65
|
isFlores,
|
|
64
|
-
operatorLabel
|
|
65
66
|
}: MobileServiceItemProps): React.ReactElement {
|
|
66
67
|
const isItemExpanded = serviceItem.id === isExpanded;
|
|
67
68
|
const isPetSeat = (Object.keys(serviceItem?.pet_seat_info) || []).length > 0;
|
|
@@ -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,28 @@ 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
|
+
isTrain?: boolean;
|
|
232
|
+
isFeatureDropDownExpand?: any;
|
|
232
233
|
setIsFeatureDropDownExpand?: (value: any) => void;
|
|
233
234
|
ticketQuantity?: number;
|
|
234
|
-
onIncreaseTicketQuantity?: (
|
|
235
|
-
|
|
235
|
+
onIncreaseTicketQuantity?: (
|
|
236
|
+
serviceItem: MobileServiceItemProps["serviceItem"],
|
|
237
|
+
) => void;
|
|
238
|
+
onDecreaseTicketQuantity?: (
|
|
239
|
+
serviceItem: MobileServiceItemProps["serviceItem"],
|
|
240
|
+
) => void;
|
|
236
241
|
cityOrigin?: { value: number; label: string };
|
|
237
242
|
cityDestination?: { value: number; label: string };
|
|
238
|
-
|
|
243
|
+
isNewUi?: boolean;
|
|
239
244
|
|
|
240
|
-
|
|
245
|
+
selectedTimeSlot?: string;
|
|
241
246
|
onTimeSlotChange?: (slot: string) => void;
|
|
242
247
|
isTimeDropdownOpen?: string | number | null;
|
|
243
248
|
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;
|
|
@@ -119,7 +119,7 @@ function DateTimeSection({
|
|
|
119
119
|
metaData={metaData}
|
|
120
120
|
colors={colors}
|
|
121
121
|
>
|
|
122
|
-
<span className="cursor-pointer bold-text capitalize">
|
|
122
|
+
<span className="cursor-pointer bold-text capitalize whitespace-nowrap">
|
|
123
123
|
{DateService.getServiceItemDate(serviceItem.travel_date)}
|
|
124
124
|
</span>
|
|
125
125
|
</StageTooltip>
|
|
@@ -134,7 +134,7 @@ function DateTimeSection({
|
|
|
134
134
|
metaData={metaData}
|
|
135
135
|
colors={colors}
|
|
136
136
|
>
|
|
137
|
-
<span className="cursor-pointer bold-text capitalize">
|
|
137
|
+
<span className="cursor-pointer bold-text capitalize whitespace-nowrap">
|
|
138
138
|
{DateService.getServiceItemDate(serviceItem.arrival_date)}
|
|
139
139
|
</span>
|
|
140
140
|
</StageTooltip>
|
|
@@ -169,7 +169,7 @@ function DateTimeSection({
|
|
|
169
169
|
metaData={metaData}
|
|
170
170
|
colors={colors}
|
|
171
171
|
>
|
|
172
|
-
<div className="font-[900] bold-text">
|
|
172
|
+
<div className="font-[900] bold-text whitespace-nowrap">
|
|
173
173
|
{isLinatal ? (
|
|
174
174
|
<>
|
|
175
175
|
{cleanedDepTime} <span>{hasPM ? "PM" : hasAM ? "AM" : ""}</span>
|
|
@@ -193,7 +193,7 @@ function DateTimeSection({
|
|
|
193
193
|
metaData={metaData}
|
|
194
194
|
colors={colors}
|
|
195
195
|
>
|
|
196
|
-
<div className="font-[900] bold-text">
|
|
196
|
+
<div className="font-[900] bold-text whitespace-nowrap">
|
|
197
197
|
{removeArrivalTime
|
|
198
198
|
? "\u00A0"
|
|
199
199
|
: serviceItem.arr_time
|
|
@@ -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
|
tooltipColor?: string;
|
|
25
36
|
}
|
|
26
37
|
|
|
@@ -32,6 +43,8 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
|
32
43
|
let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
|
|
33
44
|
label: val?.label,
|
|
34
45
|
price: val?.fare,
|
|
46
|
+
key: val?.key,
|
|
47
|
+
apiSeatType: val?.apiSeatType || val?.api_seat_type,
|
|
35
48
|
}));
|
|
36
49
|
|
|
37
50
|
seatTypesWithPrices.sort((a, b) => a.price - b.price);
|
|
@@ -39,7 +52,7 @@ function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
|
39
52
|
return seatTypesWithPrices;
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
function getSortedSeatTypes(seatTypes: SeatType[]) {
|
|
55
|
+
function getSortedSeatTypes(seatTypes: SeatType[], isTrain: any) {
|
|
43
56
|
if (!seatTypes?.length) {
|
|
44
57
|
return [{ label: "Salon cama", price: 0 }];
|
|
45
58
|
}
|
|
@@ -53,7 +66,9 @@ function getSortedSeatTypes(seatTypes: SeatType[]) {
|
|
|
53
66
|
seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
|
|
54
67
|
}
|
|
55
68
|
|
|
56
|
-
|
|
69
|
+
if (!isTrain) {
|
|
70
|
+
seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
|
|
71
|
+
}
|
|
57
72
|
|
|
58
73
|
const seenPrices = new Set<number>();
|
|
59
74
|
seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
|
|
@@ -98,16 +113,19 @@ function SeatSection({
|
|
|
98
113
|
priceColor,
|
|
99
114
|
currencySign,
|
|
100
115
|
removeDuplicateSeats,
|
|
116
|
+
selectedSeatKey,
|
|
117
|
+
onSeatSelect,
|
|
101
118
|
isPeru,
|
|
102
119
|
serviceItem,
|
|
103
120
|
renderIcon,
|
|
104
121
|
dpSeatColor,
|
|
105
122
|
discountSeatPriceColor,
|
|
123
|
+
isTrain,
|
|
124
|
+
topLabelColor,
|
|
106
125
|
tooltipColor,
|
|
107
126
|
}: SeatSectionProps): React.ReactElement {
|
|
108
127
|
const uniqueSeats = getUniqueSeats(seatTypes);
|
|
109
|
-
const sortedSeatTypes = getSortedSeatTypes(seatTypes);
|
|
110
|
-
console.log("🚀 ~ SeatSection ~ sortedSeatTypes:", sortedSeatTypes);
|
|
128
|
+
const sortedSeatTypes = getSortedSeatTypes(seatTypes, isTrain);
|
|
111
129
|
const numberOfSeats = getNumberOfSeats(seatTypes);
|
|
112
130
|
const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
|
|
113
131
|
|
|
@@ -119,22 +137,68 @@ function SeatSection({
|
|
|
119
137
|
const renderSeatNames = () => {
|
|
120
138
|
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
121
139
|
|
|
122
|
-
return seats.map((val, key: number) =>
|
|
123
|
-
SEAT_EXCEPTIONS.includes(val.label) ? null : (
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
140
|
+
return seats.map((val, key: number) => {
|
|
141
|
+
return SEAT_EXCEPTIONS.includes(val.label) ? null : (
|
|
142
|
+
<div
|
|
143
|
+
className="flex items-center"
|
|
144
|
+
style={isTrain ? { cursor: "pointer" } : undefined}
|
|
145
|
+
onClick={
|
|
146
|
+
isTrain && !isSoldOut
|
|
147
|
+
? () =>
|
|
148
|
+
val.label === selectedSeatKey
|
|
149
|
+
? onSeatSelect?.(null, 0, "", "")
|
|
150
|
+
: onSeatSelect?.(
|
|
151
|
+
val.label,
|
|
152
|
+
val.price,
|
|
153
|
+
val.key,
|
|
154
|
+
(val as any).apiSeatType,
|
|
155
|
+
)
|
|
156
|
+
: undefined
|
|
157
|
+
}
|
|
129
158
|
>
|
|
130
|
-
{
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
159
|
+
{isTrain && (
|
|
160
|
+
<div
|
|
161
|
+
style={{
|
|
162
|
+
border: `1px solid ${val.label === selectedSeatKey ? topLabelColor : "#ccc"}`,
|
|
163
|
+
borderRadius: "50%",
|
|
164
|
+
width: "14px",
|
|
165
|
+
height: "14px",
|
|
166
|
+
minWidth: "14px",
|
|
167
|
+
marginRight: "10px",
|
|
168
|
+
display: "flex",
|
|
169
|
+
alignItems: "center",
|
|
170
|
+
justifyContent: "center",
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
{val.label === selectedSeatKey && (
|
|
174
|
+
<div
|
|
175
|
+
style={{
|
|
176
|
+
backgroundColor: topLabelColor,
|
|
177
|
+
borderRadius: "50%",
|
|
178
|
+
width: "7px",
|
|
179
|
+
height: "7px",
|
|
180
|
+
}}
|
|
181
|
+
/>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
)}
|
|
185
|
+
<span
|
|
186
|
+
key={key}
|
|
187
|
+
className={`flex items-center justify-between text-[13.33px] whitespace-nowrap ${
|
|
188
|
+
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
189
|
+
}`}
|
|
190
|
+
>
|
|
191
|
+
{typeof val.label === "string" || typeof val.label === "number"
|
|
192
|
+
? removeDuplicateSeats && isPeru
|
|
193
|
+
? CommonService.truncateSeatLabel(val.label)
|
|
194
|
+
: isTrain
|
|
195
|
+
? CommonService.capitalize(String(val.label))
|
|
196
|
+
: val.label
|
|
197
|
+
: null}
|
|
198
|
+
</span>
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
});
|
|
138
202
|
};
|
|
139
203
|
|
|
140
204
|
const renderSeatPrices = () => {
|
|
@@ -151,7 +215,10 @@ function SeatSection({
|
|
|
151
215
|
serviceItem,
|
|
152
216
|
);
|
|
153
217
|
return (
|
|
154
|
-
<span
|
|
218
|
+
<span
|
|
219
|
+
key={key}
|
|
220
|
+
className="flex items-center text-[13.33px] bold-text"
|
|
221
|
+
>
|
|
155
222
|
{formatPrice(discountedPrice)}
|
|
156
223
|
</span>
|
|
157
224
|
);
|
|
@@ -235,6 +302,25 @@ function SeatSection({
|
|
|
235
302
|
}
|
|
236
303
|
return null;
|
|
237
304
|
})();
|
|
305
|
+
console.log("🚀 ~ SeatSection ~ serviceItem:", serviceItem);
|
|
306
|
+
|
|
307
|
+
// Hide the % OFF badge when max_discount is capping the percentage discount
|
|
308
|
+
// (i.e. both percentage and max_discount exist, and the raw % amount exceeds the cap)
|
|
309
|
+
const isMaxDiscountApplied = (() => {
|
|
310
|
+
const { discount_type, discount_value, max_discount } = serviceItem ?? {};
|
|
311
|
+
if (
|
|
312
|
+
discount_type === "percentage" &&
|
|
313
|
+
typeof discount_value === "number" &&
|
|
314
|
+
max_discount != null &&
|
|
315
|
+
max_discount > 0 &&
|
|
316
|
+
discountSeat
|
|
317
|
+
) {
|
|
318
|
+
const rawPercentageDiscount =
|
|
319
|
+
(discountSeat.originalPrice * discount_value) / 100;
|
|
320
|
+
return rawPercentageDiscount > max_discount;
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
})();
|
|
238
324
|
|
|
239
325
|
const renderLabels = () => {
|
|
240
326
|
if (isPeru) {
|
|
@@ -286,7 +372,9 @@ function SeatSection({
|
|
|
286
372
|
|
|
287
373
|
<span className="text-[13.33px] flex flex-col">
|
|
288
374
|
{operatorServiceName ? (
|
|
289
|
-
<span className="text-[13.33px]">
|
|
375
|
+
<span className="text-[13.33px] whitespace-nowrap">
|
|
376
|
+
{seatLabel}
|
|
377
|
+
</span>
|
|
290
378
|
) : (
|
|
291
379
|
<span className="text-[13.33px]">Desde</span>
|
|
292
380
|
)}
|
|
@@ -490,7 +578,7 @@ function SeatSection({
|
|
|
490
578
|
className="col-start-2 row-start-1 flex items-center justify-center absolute"
|
|
491
579
|
style={{ top: "-22px", left: "50%", transform: "translateX(-50%)" }}
|
|
492
580
|
>
|
|
493
|
-
{discountValue != null && (
|
|
581
|
+
{discountValue != null && !isMaxDiscountApplied && (
|
|
494
582
|
<span
|
|
495
583
|
className="rounded-[100px] bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white"
|
|
496
584
|
style={{
|
|
@@ -509,7 +597,7 @@ function SeatSection({
|
|
|
509
597
|
style={{ textAlign: "center" }}
|
|
510
598
|
>
|
|
511
599
|
<span
|
|
512
|
-
className="text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative"
|
|
600
|
+
className="text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative whitespace-nowrap"
|
|
513
601
|
style={{
|
|
514
602
|
position: "relative",
|
|
515
603
|
}}
|
|
@@ -571,7 +659,9 @@ function SeatSection({
|
|
|
571
659
|
color: isSoldOut ? "#c0c0c0" : priceColor,
|
|
572
660
|
top: 0,
|
|
573
661
|
bottom: 0,
|
|
574
|
-
left:
|
|
662
|
+
left: isTrain
|
|
663
|
+
? "73%"
|
|
664
|
+
: "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
|
|
575
665
|
right: 0,
|
|
576
666
|
justifyContent: isCentered ? "center" : "",
|
|
577
667
|
gap: "10px",
|
|
@@ -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,47 @@ 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
|
-
|
|
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 whitespace-nowrap">
|
|
102
|
+
{formattedDate}
|
|
103
|
+
</span>
|
|
104
|
+
<div className={`absolute left-[50%] ${dotPositionClass}`}>•</div>
|
|
105
|
+
<div className="font-[900] relative black-text whitespace-nowrap">
|
|
106
|
+
{timeContent}
|
|
90
107
|
</div>
|
|
91
|
-
|
|
92
|
-
</div>
|
|
93
|
-
<div
|
|
94
|
-
className="flex items-center relative capitalize justify-between"
|
|
95
|
-
style={{ flex: 1 }}
|
|
96
|
-
>
|
|
97
|
-
<span className="cursor-pointer black-text">
|
|
98
|
-
{formattedDate}
|
|
99
|
-
</span>
|
|
100
|
-
<div className={`absolute left-[50%] ${dotPositionClass}`}>•</div>
|
|
101
|
-
<div className="font-[900] relative black-text">{timeContent}</div>
|
|
108
|
+
</div>
|
|
102
109
|
</div>
|
|
103
110
|
</div>
|
|
104
|
-
|
|
111
|
+
);
|
|
105
112
|
};
|
|
106
113
|
|
|
107
114
|
function DateTimeSectionMobile({
|
|
@@ -127,6 +134,7 @@ function DateTimeSectionMobile({
|
|
|
127
134
|
tooltipBgColor,
|
|
128
135
|
showLastSeats,
|
|
129
136
|
discountSeatPriceColor,
|
|
137
|
+
isTrain,
|
|
130
138
|
}: DateTimeSectionMobileProps): React.ReactElement {
|
|
131
139
|
const { cleaned: cleanedDepTime, hasAM, hasPM } = getCleanedDepTime(depTime);
|
|
132
140
|
|
|
@@ -153,8 +161,12 @@ function DateTimeSectionMobile({
|
|
|
153
161
|
>
|
|
154
162
|
{/* DATE AND TIME */}
|
|
155
163
|
<div
|
|
156
|
-
className=
|
|
157
|
-
style={{
|
|
164
|
+
className={`flex flex-col gap-[4px] w-[50%] ${isTrain ? "justify-center" : "justify-between"}`}
|
|
165
|
+
style={{
|
|
166
|
+
justifyContent: isCiva && "center",
|
|
167
|
+
minHeight: isTrain ? undefined : "2.5rem",
|
|
168
|
+
alignSelf: isTrain ? "stretch" : undefined,
|
|
169
|
+
}}
|
|
158
170
|
>
|
|
159
171
|
<TimeRow
|
|
160
172
|
label={orignLabel}
|
|
@@ -198,6 +210,7 @@ function DateTimeSectionMobile({
|
|
|
198
210
|
tooltipBgColor={tooltipBgColor}
|
|
199
211
|
showLastSeats={showLastSeats}
|
|
200
212
|
discountSeatPriceColor={discountSeatPriceColor}
|
|
213
|
+
isTrain={isTrain}
|
|
201
214
|
/>
|
|
202
215
|
</div>
|
|
203
216
|
);
|