kupos-ui-components-lib 9.1.2 → 9.1.4
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/README copy.md +67 -223
- package/dist/assets/images/anims/service_list/directo.json +1 -1
- package/dist/components/FilterBar/FilterBarDesktop.d.ts +1 -1
- package/dist/components/FilterBar/FilterBarDesktop.js +38 -3
- package/dist/components/FilterBar/tyoes.d.ts +1 -0
- package/dist/components/ServiceItem/PeruServiceItemDesktop.js +1 -1
- package/dist/components/ServiceItem/RatingHover.js +33 -32
- package/dist/components/ServiceItem/ServiceItemDesktop.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemDesktop.js +267 -147
- package/dist/components/ServiceItem/ServiceItemMobile.d.ts +1 -1
- package/dist/components/ServiceItem/ServiceItemMobile.js +278 -87
- package/dist/components/ServiceItem/mobileTypes.d.ts +0 -5
- package/dist/components/ServiceItem/types.d.ts +0 -7
- package/dist/styles.css +32 -131
- package/dist/ui/AmenitiesBlock.js +30 -23
- package/dist/ui/DurationBlock.js +4 -4
- package/dist/ui/FlexibleBlock.js +6 -5
- package/dist/ui/PetBlock.js +3 -1
- package/dist/ui/RatingBlock.d.ts +1 -9
- package/dist/ui/RatingBlock.js +3 -7
- package/dist/utils/CommonService.d.ts +1 -1
- package/dist/utils/CommonService.js +0 -2
- package/package.json +1 -2
- package/src/assets/images/anims/service_list/directo.json +1 -1
- package/src/components/FilterBar/FilterBarDesktop.tsx +46 -3
- package/src/components/FilterBar/tyoes.ts +1 -0
- package/src/components/ServiceItem/PeruServiceItemDesktop.tsx +0 -1
- package/src/components/ServiceItem/RatingHover.tsx +45 -44
- package/src/components/ServiceItem/ServiceItemDesktop.tsx +537 -313
- package/src/components/ServiceItem/ServiceItemMobile.tsx +530 -213
- package/src/components/ServiceItem/mobileTypes.ts +0 -5
- package/src/components/ServiceItem/types.ts +0 -7
- package/src/ui/AmenitiesBlock.tsx +29 -50
- package/src/ui/DurationBlock.tsx +4 -4
- package/src/ui/FlexibleBlock.tsx +5 -6
- package/src/ui/PetBlock.tsx +2 -2
- package/src/ui/RatingBlock.tsx +6 -18
- package/src/utils/CommonService.ts +0 -2
- package/src/assets/images/anims/service_list/bomb.json +0 -1
- package/src/ui/BottomAmenities/BottomAmenities.tsx +0 -110
- package/src/ui/DateTimeSection/DateTimeSection.tsx +0 -207
- package/src/ui/DirectoBlock.tsx +0 -31
- package/src/ui/ExpendedDropDown/ExpandedDropdown.tsx +0 -103
- package/src/ui/KuposButton/KuposButton.tsx +0 -48
- package/src/ui/SeatSection/SeatSection.tsx +0 -207
- package/src/ui/TopAmenities/TopAmenities.tsx +0 -127
- package/src/ui/mobileweb/BottomAmenitiesMobile.tsx +0 -169
- package/src/ui/mobileweb/DateTimeSectionMobile.tsx +0 -192
- package/src/ui/mobileweb/ExpandedDropdownMobile.tsx +0 -56
- package/src/ui/mobileweb/SeatSectionMobile.tsx +0 -256
- package/src/ui/mobileweb/TopAmenitieMobile.tsx +0 -126
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import DateService from "../../utils/DateService";
|
|
3
|
-
import StageTooltip from "../StagesTooltip";
|
|
4
|
-
|
|
5
|
-
interface DateTimeSectionProps {
|
|
6
|
-
serviceItem: {
|
|
7
|
-
icons?: { origin?: string; destination?: string; [key: string]: any };
|
|
8
|
-
boarding_stages?: string;
|
|
9
|
-
dropoff_stages?: string;
|
|
10
|
-
travel_date: string;
|
|
11
|
-
arrival_date: string;
|
|
12
|
-
dep_time: string;
|
|
13
|
-
arr_time: string;
|
|
14
|
-
[key: string]: any;
|
|
15
|
-
};
|
|
16
|
-
isSoldOut: boolean;
|
|
17
|
-
isCiva?: boolean;
|
|
18
|
-
isLinatal?: boolean;
|
|
19
|
-
removeArrivalTime?: boolean;
|
|
20
|
-
orignLabel?: string;
|
|
21
|
-
destinationLabel?: string;
|
|
22
|
-
busStage?: Record<string, string>;
|
|
23
|
-
metaData?: any;
|
|
24
|
-
colors: Record<string, any>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function DateTimeSection({
|
|
28
|
-
serviceItem,
|
|
29
|
-
isSoldOut,
|
|
30
|
-
isCiva,
|
|
31
|
-
isLinatal,
|
|
32
|
-
removeArrivalTime,
|
|
33
|
-
orignLabel,
|
|
34
|
-
destinationLabel,
|
|
35
|
-
busStage,
|
|
36
|
-
metaData,
|
|
37
|
-
colors,
|
|
38
|
-
}: DateTimeSectionProps): React.ReactElement {
|
|
39
|
-
const depTime = serviceItem.dep_time || "";
|
|
40
|
-
const hasAM = depTime.includes("AM");
|
|
41
|
-
const hasPM = depTime.includes("PM");
|
|
42
|
-
|
|
43
|
-
const convertTo24Hour = (depTime: string): string => {
|
|
44
|
-
const hasAM = depTime.includes("AM");
|
|
45
|
-
const hasPM = depTime.includes("PM");
|
|
46
|
-
const [timePart] = depTime.split(/AM|PM/).map((part) => part.trim());
|
|
47
|
-
const [hour, minute] = timePart.split(":").map(Number);
|
|
48
|
-
const pad = (n: number) => (n < 10 ? "0" + n : String(n));
|
|
49
|
-
|
|
50
|
-
if (hasAM) {
|
|
51
|
-
return `${pad(hour === 12 ? 0 : hour)}:${pad(minute)}`;
|
|
52
|
-
}
|
|
53
|
-
if (hasPM) {
|
|
54
|
-
return `${hour === 12 ? hour : hour + 12}:${pad(minute)}`;
|
|
55
|
-
}
|
|
56
|
-
return timePart;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const cleanedDepTime = convertTo24Hour(depTime);
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<div
|
|
63
|
-
className={`min-h-[2.2rem] grid grid-cols-[26px_auto_26%_1fr] gap-x-4 items-center text-[13.33px] ${
|
|
64
|
-
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
65
|
-
}`}
|
|
66
|
-
style={{
|
|
67
|
-
gridTemplateRows: "1fr",
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
{/* ICONS COLUMN */}
|
|
71
|
-
<div className="flex flex-col gap-[4px]">
|
|
72
|
-
{/* Origin Icon */}
|
|
73
|
-
{orignLabel ? (
|
|
74
|
-
<div className="w-[60px] h-[20px] flex items-center">
|
|
75
|
-
{orignLabel}
|
|
76
|
-
</div>
|
|
77
|
-
) : (
|
|
78
|
-
<div className="h-[20px] flex items-center">
|
|
79
|
-
<img
|
|
80
|
-
src={serviceItem.icons?.origin}
|
|
81
|
-
alt="origin"
|
|
82
|
-
className={`w-[16px] h-auto mr-[8px] ${
|
|
83
|
-
isSoldOut ? "grayscale" : ""
|
|
84
|
-
}`}
|
|
85
|
-
/>
|
|
86
|
-
</div>
|
|
87
|
-
)}
|
|
88
|
-
|
|
89
|
-
{/* Destination Icon */}
|
|
90
|
-
{!isCiva &&
|
|
91
|
-
(destinationLabel ? (
|
|
92
|
-
<div className="w-[60px] h-[20px] flex items-center">
|
|
93
|
-
{destinationLabel}
|
|
94
|
-
</div>
|
|
95
|
-
) : (
|
|
96
|
-
<div className="h-[20px] flex items-center">
|
|
97
|
-
<img
|
|
98
|
-
src={serviceItem.icons?.destination}
|
|
99
|
-
className={`w-[16px] h-auto mr-[8px] ${
|
|
100
|
-
isSoldOut ? "grayscale" : ""
|
|
101
|
-
}`}
|
|
102
|
-
style={{ opacity: isSoldOut ? 0.5 : 1 }}
|
|
103
|
-
/>
|
|
104
|
-
</div>
|
|
105
|
-
))}
|
|
106
|
-
</div>
|
|
107
|
-
|
|
108
|
-
{/* DATES COLUMN */}
|
|
109
|
-
<div className="flex flex-col gap-[4px]">
|
|
110
|
-
{/* Departure Date */}
|
|
111
|
-
<StageTooltip
|
|
112
|
-
stageData={serviceItem.boarding_stages}
|
|
113
|
-
direction={1}
|
|
114
|
-
terminals={busStage}
|
|
115
|
-
serviceItem={serviceItem}
|
|
116
|
-
metaData={metaData}
|
|
117
|
-
colors={colors}
|
|
118
|
-
>
|
|
119
|
-
<span className="cursor-pointer bold-text capitalize">
|
|
120
|
-
{DateService.getServiceItemDate(serviceItem.travel_date)}
|
|
121
|
-
</span>
|
|
122
|
-
</StageTooltip>
|
|
123
|
-
|
|
124
|
-
{/* Arrival Date */}
|
|
125
|
-
{!isCiva && (
|
|
126
|
-
<StageTooltip
|
|
127
|
-
stageData={serviceItem.boarding_stages}
|
|
128
|
-
direction={1}
|
|
129
|
-
terminals={busStage}
|
|
130
|
-
serviceItem={serviceItem}
|
|
131
|
-
metaData={metaData}
|
|
132
|
-
colors={colors}
|
|
133
|
-
>
|
|
134
|
-
<span className="cursor-pointer bold-text capitalize">
|
|
135
|
-
{DateService.getServiceItemDate(serviceItem.arrival_date)}
|
|
136
|
-
</span>
|
|
137
|
-
</StageTooltip>
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
|
|
141
|
-
{/* DOTS COLUMN */}
|
|
142
|
-
<div className="flex flex-col gap-[4px] items-center">
|
|
143
|
-
{/* Departure Dot */}
|
|
144
|
-
<div className="h-[20px] flex items-center justify-center">
|
|
145
|
-
<div>•</div>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
{/* Arrival Dot */}
|
|
149
|
-
{!isCiva && (
|
|
150
|
-
<div className="h-[20px] flex items-center justify-center">
|
|
151
|
-
{removeArrivalTime ? null : serviceItem.arr_time ? (
|
|
152
|
-
<div>•</div>
|
|
153
|
-
) : null}
|
|
154
|
-
</div>
|
|
155
|
-
)}
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
{/* TIMES COLUMN */}
|
|
159
|
-
<div className="flex flex-col gap-[4px]">
|
|
160
|
-
{/* Departure Time */}
|
|
161
|
-
<StageTooltip
|
|
162
|
-
stageData={serviceItem.dropoff_stages}
|
|
163
|
-
direction={2}
|
|
164
|
-
terminals={busStage}
|
|
165
|
-
serviceItem={serviceItem}
|
|
166
|
-
metaData={metaData}
|
|
167
|
-
colors={colors}
|
|
168
|
-
>
|
|
169
|
-
<div className="font-[900] bold-text">
|
|
170
|
-
{isLinatal ? (
|
|
171
|
-
<>
|
|
172
|
-
{cleanedDepTime} <span>{hasPM ? "PM" : hasAM ? "AM" : ""}</span>
|
|
173
|
-
{!serviceItem?.dep_time.includes("AM") &&
|
|
174
|
-
!serviceItem?.dep_time.includes("PM") &&
|
|
175
|
-
DateService.ampmOnly(serviceItem.dep_time)}
|
|
176
|
-
</>
|
|
177
|
-
) : (
|
|
178
|
-
DateService.formatTime(serviceItem.dep_time)
|
|
179
|
-
)}
|
|
180
|
-
</div>
|
|
181
|
-
</StageTooltip>
|
|
182
|
-
|
|
183
|
-
{/* Arrival Time */}
|
|
184
|
-
{!isCiva && (
|
|
185
|
-
<StageTooltip
|
|
186
|
-
stageData={serviceItem.dropoff_stages}
|
|
187
|
-
direction={2}
|
|
188
|
-
terminals={busStage}
|
|
189
|
-
serviceItem={serviceItem}
|
|
190
|
-
metaData={metaData}
|
|
191
|
-
colors={colors}
|
|
192
|
-
>
|
|
193
|
-
<div className="font-[900] bold-text">
|
|
194
|
-
{removeArrivalTime
|
|
195
|
-
? "\u00A0"
|
|
196
|
-
: serviceItem.arr_time
|
|
197
|
-
? DateService.formatTime(serviceItem.arr_time)
|
|
198
|
-
: "\u00A0"}
|
|
199
|
-
</div>
|
|
200
|
-
</StageTooltip>
|
|
201
|
-
)}
|
|
202
|
-
</div>
|
|
203
|
-
</div>
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export default DateTimeSection;
|
package/src/ui/DirectoBlock.tsx
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import LottiePlayer from "../assets/LottiePlayer";
|
|
3
|
-
|
|
4
|
-
const DirectoBlock = ({ translation, isSoldOut, colors, getAnimationIcon }) => (
|
|
5
|
-
<div className="flex items-center relative whitespace-nowrap mt-[3px]">
|
|
6
|
-
<div className={`w-[18px] mr-[4px] ${isSoldOut ? "grayscale" : ""}`}>
|
|
7
|
-
<LottiePlayer
|
|
8
|
-
animationData={getAnimationIcon("directoAnim")}
|
|
9
|
-
width="14px"
|
|
10
|
-
height="14px"
|
|
11
|
-
/>
|
|
12
|
-
</div>
|
|
13
|
-
|
|
14
|
-
<div className="group text-[13.33px] cursor-pointer">
|
|
15
|
-
{translation?.directService}
|
|
16
|
-
<div
|
|
17
|
-
className="hidden group-hover:block absolute top-[24px] left-1/2 -translate-x-1/2 text-white p-3 rounded-[14px] whitespace-nowrap w-fit z-10 mt-2.5 text-center shadow-service text-[12px]"
|
|
18
|
-
style={{ backgroundColor: colors.bottomStripColor }}
|
|
19
|
-
>
|
|
20
|
-
{/* Tooltip arrow */}
|
|
21
|
-
<div
|
|
22
|
-
className="tooltip-arrow absolute -top-[7px] left-1/2 -translate-x-1/2 w-0 h-0 border-l-8 border-r-8 border-b-8 border-l-transparent border-r-transparent"
|
|
23
|
-
style={{ borderBottomColor: colors.bottomStripColor }}
|
|
24
|
-
></div>
|
|
25
|
-
{translation?.directServiceText}
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
export default DirectoBlock;
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import LottiePlayer from "../../assets/LottiePlayer";
|
|
3
|
-
|
|
4
|
-
interface ExpandedDropdownProps {
|
|
5
|
-
serviceItem: {
|
|
6
|
-
change_ticket_hours?: number;
|
|
7
|
-
pet_seat_info?: Record<string, any>;
|
|
8
|
-
is_change_ticket?: boolean;
|
|
9
|
-
is_tracking_enabled?: boolean;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function ExpandedDropdown({
|
|
14
|
-
serviceItem,
|
|
15
|
-
}: ExpandedDropdownProps): React.ReactElement {
|
|
16
|
-
const hasPetInfo =
|
|
17
|
-
serviceItem.pet_seat_info &&
|
|
18
|
-
Object.keys(serviceItem.pet_seat_info).length > 0;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div
|
|
22
|
-
className="px-[15px] pt-[26px] pb-[14px] -mt-[16px] pt-[35px] relative -z-9"
|
|
23
|
-
style={{
|
|
24
|
-
backgroundColor: "#ffefef",
|
|
25
|
-
borderRadius: "0 0 10px 10px",
|
|
26
|
-
// border: showPromo ? `1px solid ${colors.priceColor}` : "1px solid #ccc",
|
|
27
|
-
// border: `1px solid ${colors.priceColor}`,
|
|
28
|
-
|
|
29
|
-
// borderTop: "none",
|
|
30
|
-
}}
|
|
31
|
-
>
|
|
32
|
-
{/* <div className="flex flex-col gap-[12px] text-[13px] text-[#464647]">
|
|
33
|
-
{hasPetInfo && (
|
|
34
|
-
<div className="flex items-center gap-[10px]">
|
|
35
|
-
<LottiePlayer
|
|
36
|
-
animationData={getAnimationIcon("petFriendlyAnim")}
|
|
37
|
-
width="20px"
|
|
38
|
-
height="20px"
|
|
39
|
-
/>
|
|
40
|
-
<div className="h-auto mr-[4px] text-[13px] text-[#464647] bold-text">
|
|
41
|
-
<span>{translation?.petFriendly}</span>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
)}
|
|
45
|
-
{serviceItem.is_change_ticket && (
|
|
46
|
-
<div className="flex items-center gap-[10px]">
|
|
47
|
-
<LottiePlayer
|
|
48
|
-
animationData={getAnimationIcon("flexibleIcon")}
|
|
49
|
-
width="20px"
|
|
50
|
-
height="20px"
|
|
51
|
-
/>
|
|
52
|
-
<div className="h-auto mr-[4px] text-[13px] text-[#464647] bold-text">
|
|
53
|
-
<span>{translation?.flexible}</span>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
)}
|
|
57
|
-
{serviceItem?.is_tracking_enabled && (
|
|
58
|
-
<div className={`${grayscaleClass} flex items-center gap-[10px]`}>
|
|
59
|
-
<LottiePlayer
|
|
60
|
-
animationData={getAnimationIcon("locationAnim")}
|
|
61
|
-
width="20px"
|
|
62
|
-
height="20px"
|
|
63
|
-
/>
|
|
64
|
-
<div className="h-auto mr-[4px] text-[13px] text-[#464647] bold-text">
|
|
65
|
-
<span>{"GPS Tracker"}</span>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
)}
|
|
69
|
-
</div> */}
|
|
70
|
-
<div
|
|
71
|
-
className="flex flex-col gap-[10px] text-[13px] text-[#464647]"
|
|
72
|
-
style={{ lineHeight: 1.6 }}
|
|
73
|
-
>
|
|
74
|
-
<div className="flex gap-[8px] text-[13.33px]">
|
|
75
|
-
<span style={{ marginTop: "2px" }}>•</span>
|
|
76
|
-
<span>
|
|
77
|
-
<span className="bold-text">Políticas de anulación:</span> Tu pasaje
|
|
78
|
-
puede ser anulado de forma online{" "}
|
|
79
|
-
<span className="bold-text">
|
|
80
|
-
hasta {serviceItem?.change_ticket_hours ?? 6} horas antes
|
|
81
|
-
</span>{" "}
|
|
82
|
-
de la salida del bus. Al anular tu pasaje recibirás una devolución
|
|
83
|
-
del 85% del monto de tu compra.
|
|
84
|
-
</span>
|
|
85
|
-
</div>
|
|
86
|
-
<div className="flex gap-[8px]">
|
|
87
|
-
<span style={{ marginTop: "2px" }}>•</span>
|
|
88
|
-
<span>
|
|
89
|
-
<span className="bold-text">Políticas de cambios:</span> Tu pasaje
|
|
90
|
-
puede ser cambiado de manera online{" "}
|
|
91
|
-
<span className="bold-text">
|
|
92
|
-
hasta {serviceItem?.change_ticket_hours ?? 6} horas antes
|
|
93
|
-
</span>{" "}
|
|
94
|
-
de la salida del bus. El monto será reembolsado a tu billetera
|
|
95
|
-
kupospay.
|
|
96
|
-
</span>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export default ExpandedDropdown;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
interface KuposButtonProps {
|
|
4
|
-
isSoldOut: boolean;
|
|
5
|
-
isLoading: boolean;
|
|
6
|
-
buttonColor?: string;
|
|
7
|
-
buyLabel?: string;
|
|
8
|
-
soldOutLabel?: string;
|
|
9
|
-
soldOutIcon?: React.ReactNode;
|
|
10
|
-
onClick: () => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function KuposButton({
|
|
14
|
-
isSoldOut,
|
|
15
|
-
isLoading,
|
|
16
|
-
buttonColor,
|
|
17
|
-
buyLabel,
|
|
18
|
-
soldOutLabel,
|
|
19
|
-
soldOutIcon,
|
|
20
|
-
onClick,
|
|
21
|
-
}: KuposButtonProps): React.ReactElement {
|
|
22
|
-
const isDisabled = isLoading || isSoldOut;
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<button
|
|
26
|
-
onClick={() => (!isSoldOut ? onClick() : null)}
|
|
27
|
-
disabled={isLoading}
|
|
28
|
-
className="w-full py-[12px] text-[12px] font-bold text-white rounded-[14px] border-none px-[20px] flex items-center justify-center"
|
|
29
|
-
style={{
|
|
30
|
-
backgroundColor: isDisabled ? "lightgray" : buttonColor,
|
|
31
|
-
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
32
|
-
}}
|
|
33
|
-
>
|
|
34
|
-
<span className="min-w-[75px] flex justify-center items-center bold-text uppercase">
|
|
35
|
-
{isSoldOut ? soldOutIcon : null}
|
|
36
|
-
{isLoading ? (
|
|
37
|
-
<span className="loader-circle"></span>
|
|
38
|
-
) : !isSoldOut ? (
|
|
39
|
-
buyLabel
|
|
40
|
-
) : (
|
|
41
|
-
soldOutLabel
|
|
42
|
-
)}
|
|
43
|
-
</span>
|
|
44
|
-
</button>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export default KuposButton;
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import CommonService from "../../utils/CommonService";
|
|
3
|
-
|
|
4
|
-
const SEAT_EXCEPTIONS = ["Asiento mascota"];
|
|
5
|
-
|
|
6
|
-
interface SeatType {
|
|
7
|
-
label: string;
|
|
8
|
-
fare: number;
|
|
9
|
-
key: any;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface SeatSectionProps {
|
|
13
|
-
seatTypes: SeatType[];
|
|
14
|
-
availableSeats: number;
|
|
15
|
-
isSoldOut: boolean;
|
|
16
|
-
priceColor?: string;
|
|
17
|
-
currencySign?: string;
|
|
18
|
-
removeDuplicateSeats?: boolean;
|
|
19
|
-
isPeru?: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getAllSeatTypes(seatTypes: SeatType[]) {
|
|
23
|
-
if (!seatTypes?.length) {
|
|
24
|
-
return [{ label: "Salon cama", price: 0 }];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let seatTypesWithPrices = seatTypes.filter(Boolean).map((val) => ({
|
|
28
|
-
label: val?.label,
|
|
29
|
-
price: val?.fare,
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
seatTypesWithPrices.sort((a, b) => a.price - b.price);
|
|
33
|
-
|
|
34
|
-
return seatTypesWithPrices;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getSortedSeatTypes(seatTypes: SeatType[]) {
|
|
38
|
-
if (!seatTypes?.length) {
|
|
39
|
-
return [{ label: "Salon cama", price: 0 }];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let seatTypesWithPrices = getAllSeatTypes(seatTypes);
|
|
43
|
-
const premiumIndex = seatTypesWithPrices.findIndex(
|
|
44
|
-
(item) => item.label === "Premium",
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
if (premiumIndex >= 3) {
|
|
48
|
-
seatTypesWithPrices[2] = seatTypesWithPrices[premiumIndex];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
seatTypesWithPrices = seatTypesWithPrices.slice(0, 2);
|
|
52
|
-
|
|
53
|
-
const seenPrices = new Set<number>();
|
|
54
|
-
seatTypesWithPrices = seatTypesWithPrices.filter((seat) => {
|
|
55
|
-
if (seenPrices.has(seat.price)) return false;
|
|
56
|
-
seenPrices.add(seat.price);
|
|
57
|
-
return true;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return seatTypesWithPrices;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function getUniqueSeats(seatTypes: SeatType[]) {
|
|
64
|
-
const allSeatTypes = getAllSeatTypes(seatTypes);
|
|
65
|
-
const seatMap = new Map();
|
|
66
|
-
|
|
67
|
-
allSeatTypes.forEach((seat) => {
|
|
68
|
-
if (SEAT_EXCEPTIONS.includes(seat.label)) return;
|
|
69
|
-
|
|
70
|
-
// Only check if the label already exists in the map, don't compare prices
|
|
71
|
-
if (!seatMap.has(seat.label)) {
|
|
72
|
-
seatMap.set(seat.label, seat);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const uniqueByLabel = Array.from(seatMap.values());
|
|
77
|
-
const seenPrices = new Set<number>();
|
|
78
|
-
return uniqueByLabel.filter((seat) => {
|
|
79
|
-
if (seenPrices.has(seat.price)) return false;
|
|
80
|
-
seenPrices.add(seat.price);
|
|
81
|
-
return true;
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function getNumberOfSeats(seatTypes: SeatType[]) {
|
|
86
|
-
return seatTypes.filter((val) => !SEAT_EXCEPTIONS.includes(val.label)).length;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function SeatSection({
|
|
90
|
-
seatTypes,
|
|
91
|
-
availableSeats,
|
|
92
|
-
isSoldOut,
|
|
93
|
-
priceColor,
|
|
94
|
-
currencySign,
|
|
95
|
-
removeDuplicateSeats,
|
|
96
|
-
isPeru,
|
|
97
|
-
}: SeatSectionProps): React.ReactElement {
|
|
98
|
-
const uniqueSeats = getUniqueSeats(seatTypes);
|
|
99
|
-
const sortedSeatTypes = getSortedSeatTypes(seatTypes);
|
|
100
|
-
const numberOfSeats = getNumberOfSeats(seatTypes);
|
|
101
|
-
const isCentered = numberOfSeats < 2 || removeDuplicateSeats;
|
|
102
|
-
|
|
103
|
-
const formatPrice = (price: number) =>
|
|
104
|
-
availableSeats <= 0
|
|
105
|
-
? CommonService.currency(0, currencySign)
|
|
106
|
-
: CommonService.currency(price, currencySign);
|
|
107
|
-
|
|
108
|
-
const renderSeatNames = () => {
|
|
109
|
-
const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
|
|
110
|
-
|
|
111
|
-
return seats.map((val, key: number) =>
|
|
112
|
-
SEAT_EXCEPTIONS.includes(val.label) ? null : (
|
|
113
|
-
<span
|
|
114
|
-
key={key}
|
|
115
|
-
className={`flex items-center justify-between text-[13.33px] ${
|
|
116
|
-
isSoldOut ? "text-[#c0c0c0]" : ""
|
|
117
|
-
}`}
|
|
118
|
-
>
|
|
119
|
-
{typeof val.label === "string" || typeof val.label === "number"
|
|
120
|
-
? removeDuplicateSeats && isPeru
|
|
121
|
-
? CommonService.truncateSeatLabel(val.label)
|
|
122
|
-
: val.label
|
|
123
|
-
: null}
|
|
124
|
-
</span>
|
|
125
|
-
),
|
|
126
|
-
);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const renderSeatPrices = () => {
|
|
130
|
-
if (removeDuplicateSeats) {
|
|
131
|
-
return uniqueSeats.map((val, key) => (
|
|
132
|
-
<span key={key}>{formatPrice(val.price)}</span>
|
|
133
|
-
));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return sortedSeatTypes.map((val, key: number) =>
|
|
137
|
-
SEAT_EXCEPTIONS.includes(val.label) ? null : (
|
|
138
|
-
<span key={key} className="flex items-center text-[13.33px] bold-text">
|
|
139
|
-
{typeof val.price === "string" || typeof val.price === "number"
|
|
140
|
-
? formatPrice(val.price)
|
|
141
|
-
: null}
|
|
142
|
-
</span>
|
|
143
|
-
),
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const renderLabels = () => {
|
|
148
|
-
if (isPeru) {
|
|
149
|
-
return (
|
|
150
|
-
<>
|
|
151
|
-
<span
|
|
152
|
-
className="text-[13.33px]"
|
|
153
|
-
style={{
|
|
154
|
-
color: "#999",
|
|
155
|
-
// position: "relative",
|
|
156
|
-
// bottom: numberOfSeats ? "10px" : "",
|
|
157
|
-
}}
|
|
158
|
-
>
|
|
159
|
-
Antes
|
|
160
|
-
</span>
|
|
161
|
-
<span className="text-[13.33px]">Desde</span>
|
|
162
|
-
</>
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
return renderSeatNames();
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const strikethroughStyle: React.CSSProperties = {
|
|
169
|
-
color: "#ccc",
|
|
170
|
-
display: "flex",
|
|
171
|
-
textAlign: "end",
|
|
172
|
-
textDecoration: "line-through",
|
|
173
|
-
...(isPeru
|
|
174
|
-
? { position: "relative", top: 0 }
|
|
175
|
-
: { position: "absolute", top: isCentered ? "-10px" : "-18px" }),
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
return (
|
|
179
|
-
<div
|
|
180
|
-
className="relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]"
|
|
181
|
-
style={isCentered ? { alignItems: "center" } : {}}
|
|
182
|
-
>
|
|
183
|
-
<div className="flex flex-col justify-between" style={{ gap: "10px" }}>
|
|
184
|
-
{renderLabels()}
|
|
185
|
-
</div>
|
|
186
|
-
<div
|
|
187
|
-
className="flex flex-col justify-between absolute inset-y-0 right-0 left-1/2 h-full"
|
|
188
|
-
style={{
|
|
189
|
-
color: isSoldOut ? "#c0c0c0" : priceColor,
|
|
190
|
-
top: 0,
|
|
191
|
-
bottom: 0,
|
|
192
|
-
left: "clamp(60%, 65% + (100vw - 1300px) * 0.1, 65%)",
|
|
193
|
-
right: 0,
|
|
194
|
-
justifyContent: isCentered ? "center" : "",
|
|
195
|
-
gap: "10px",
|
|
196
|
-
}}
|
|
197
|
-
>
|
|
198
|
-
<span className="text-[13.33px]" style={strikethroughStyle}>
|
|
199
|
-
$1.000
|
|
200
|
-
</span>
|
|
201
|
-
{renderSeatPrices()}
|
|
202
|
-
</div>
|
|
203
|
-
</div>
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export default SeatSection;
|