kupos-ui-components-lib 9.3.1 → 9.3.2

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.
@@ -31,54 +31,36 @@ function ExpandedDropdown({
31
31
  style={{
32
32
  backgroundColor: "#ffefef",
33
33
  borderRadius: "0 0 10px 10px",
34
- // border: showPromo ? `1px solid ${colors.priceColor}` : "1px solid #ccc",
35
- // border: `1px solid ${colors.priceColor}`,
36
-
37
- // borderTop: "none",
38
34
  }}
39
35
  >
40
- {/* <div className="flex flex-col gap-[12px] text-[13px] text-[#464647]">
41
- {hasPetInfo && (
42
- <div className="flex items-center gap-[10px]">
43
- <LottiePlayer
44
- animationData={getAnimationIcon("petFriendlyAnim")}
45
- width="20px"
46
- height="20px"
47
- />
48
- <div className="h-auto mr-[4px] text-[13px] text-[#464647] bold-text">
49
- <span>{translation?.petFriendly}</span>
50
- </div>
51
- </div>
52
- )}
53
- { serviceItem.is_change_ticket && (
54
- <div className="flex items-center gap-[10px]">
55
- <LottiePlayer
56
- animationData={getAnimationIcon("flexibleIcon")}
57
- width="20px"
58
- height="20px"
59
- />
60
- <div className="h-auto mr-[4px] text-[13px] text-[#464647] bold-text">
61
- <span>{translation?.flexible}</span>
62
- </div>
36
+ <div
37
+ className="flex flex-col gap-[6px] text-[13px] text-[#464647]"
38
+ style={{ lineHeight: 1.5 }}
39
+ >
40
+ {isPeru ? null : isChangeTicket ? (
41
+ <div className="flex gap-[8px]" style={{ lineHeight: 1.5 }}>
42
+ <span style={{ marginTop: "2px" }}>•</span>
43
+ <span>
44
+ <span className="bold-text">Pasaje flexible:</span> Tu pasaje
45
+ puede ser cambiado de manera online{" "}
46
+ <span className="bold-text">
47
+ hasta {serviceItem?.change_ticket_hours || 6} horas antes
48
+ </span>{" "}
49
+ de la salida del bus. El monto será reembolsado a tu billetera
50
+ kupospay.
51
+ </span>
63
52
  </div>
64
- )}
65
- {serviceItem?.is_tracking_enabled && (
66
- <div className={`${grayscaleClass} flex items-center gap-[10px]`}>
67
- <LottiePlayer
68
- animationData={getAnimationIcon("locationAnim")}
69
- width="20px"
70
- height="20px"
71
- />
72
- <div className="h-auto mr-[4px] text-[13px] text-[#464647] bold-text">
73
- <span>{"GPS Tracker"}</span>
74
- </div>
53
+ ) : (
54
+ <div className="flex gap-[8px] text-[13.33px]">
55
+ <span style={{ marginTop: "2px" }}>•</span>
56
+ <span>
57
+ <span>
58
+ <span className="bold-text">Pasaje flexible:</span> Esta empresa
59
+ no permite cambios de pasajes.
60
+ </span>
61
+ </span>
75
62
  </div>
76
63
  )}
77
- </div> */}
78
- <div
79
- className="flex flex-col gap-[10px] text-[13px] text-[#464647]"
80
- style={{ lineHeight: 1.6 }}
81
- >
82
64
  {hasPetInfo && (
83
65
  <div className="flex items-center gap-[10px]">
84
66
  <LottiePlayer
@@ -91,6 +73,7 @@ function ExpandedDropdown({
91
73
  </div>
92
74
  </div>
93
75
  )}
76
+
94
77
  <div className="flex gap-[8px] text-[13.33px]">
95
78
  <span style={{ marginTop: "2px" }}>•</span>
96
79
  <span>
@@ -100,30 +83,6 @@ function ExpandedDropdown({
100
83
  salida del bus.
101
84
  </span>
102
85
  </div>
103
- {isPeru ? null : isChangeTicket ? (
104
- <div className="flex gap-[8px]">
105
- <span style={{ marginTop: "2px" }}>•</span>
106
- <span>
107
- <span className="bold-text">Políticas de cambios:</span> Tu pasaje
108
- puede ser cambiado de manera online{" "}
109
- <span className="bold-text">
110
- hasta {serviceItem?.change_ticket_hours || 6} horas antes
111
- </span>{" "}
112
- de la salida del bus. El monto será reembolsado a tu billetera
113
- kupospay.
114
- </span>
115
- </div>
116
- ) : (
117
- <div className="flex gap-[8px] text-[13.33px]">
118
- <span style={{ marginTop: "2px" }}>•</span>
119
- <span>
120
- <span>
121
- <span className="bold-text">Política de cambios:</span> Esta
122
- empresa no permite cambios de pasajes
123
- </span>
124
- </span>
125
- </div>
126
- )}
127
86
  </div>
128
87
  </div>
129
88
  );
@@ -18,6 +18,7 @@ interface SeatSectionProps {
18
18
  removeDuplicateSeats?: boolean;
19
19
  isPeru?: boolean;
20
20
  serviceItem?: any;
21
+ renderIcon?: (iconKey: string, size?: string) => React.ReactNode;
21
22
  }
22
23
 
23
24
  function getAllSeatTypes(seatTypes: SeatType[]) {
@@ -96,6 +97,7 @@ function SeatSection({
96
97
  removeDuplicateSeats,
97
98
  isPeru,
98
99
  serviceItem,
100
+ renderIcon,
99
101
  }: SeatSectionProps): React.ReactElement {
100
102
  const uniqueSeats = getUniqueSeats(seatTypes);
101
103
  const sortedSeatTypes = getSortedSeatTypes(seatTypes);
@@ -132,16 +134,13 @@ function SeatSection({
132
134
  if (isPeru) {
133
135
  const allSeats = getAllSeatTypes(seatTypes);
134
136
  const lowestFare = allSeats.length > 0 ? allSeats[0].price : 0;
135
- const { originalPrice, discountedPrice } =
136
- CommonService.calculateDiscountedPrice(lowestFare, serviceItem);
137
+ const { discountedPrice } = CommonService.calculateDiscountedPrice(
138
+ lowestFare,
139
+ serviceItem,
140
+ );
137
141
 
138
142
  return (
139
143
  <>
140
- {originalPrice !== discountedPrice && (
141
- <span className="text-[13.33px]" style={strikethroughStyle}>
142
- {formatPrice(originalPrice)}
143
- </span>
144
- )}
145
144
  <span className="flex items-center text-[13.33px] bold-text">
146
145
  {formatPrice(discountedPrice)}
147
146
  </span>
@@ -178,30 +177,26 @@ function SeatSection({
178
177
  });
179
178
  };
180
179
 
181
- const strikethroughStyle: React.CSSProperties = {
182
- color: "#ccc",
183
- display: "flex",
184
- textAlign: "end",
185
- textDecoration: "line-through",
186
- ...(isPeru
187
- ? { position: "relative", top: 0 }
188
- : { position: "absolute", top: isCentered ? "-10px" : "-18px" }),
189
- };
190
-
191
180
  const seats = removeDuplicateSeats ? uniqueSeats : sortedSeatTypes;
192
181
  const discountedSeats = seats.map((seat) => ({
193
182
  ...seat,
194
183
  ...CommonService.calculateDiscountedPrice(seat.price, serviceItem),
195
184
  }));
196
185
 
197
- const highestOriginalPrice = Math.max(
198
- ...discountedSeats.map((seat) => seat.originalPrice),
199
- );
200
-
201
186
  const hasDiscount = discountedSeats.some(
202
187
  (seat) => seat.originalPrice !== seat.discountedPrice,
203
188
  );
204
189
 
190
+ const discountSeat = discountedSeats
191
+ .filter((seat) => !SEAT_EXCEPTIONS.includes(seat.label))
192
+ .sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
193
+
194
+ const discountValue =
195
+ serviceItem?.discount_type === "percentage" &&
196
+ typeof serviceItem?.discount_value === "number"
197
+ ? Math.round(serviceItem.discount_value)
198
+ : null;
199
+
205
200
  const renderLabels = () => {
206
201
  if (isPeru) {
207
202
  return (
@@ -225,6 +220,55 @@ function SeatSection({
225
220
  return renderSeatNames();
226
221
  };
227
222
 
223
+ if (hasDiscount && discountSeat) {
224
+ return (
225
+ <div className="grid items-center text-[13.33px] relative">
226
+ <div className="col-start-1 row-start-2 flex items-center">
227
+ <span className="text-[13.33px] font-normal leading-[22px] text-[#c2c2c2]">
228
+ Antes
229
+ </span>
230
+ </div>
231
+
232
+ <div className="col-start-1 row-start-3 flex h-[30px] items-end">
233
+ <span className="text-[13.33px] font-normal leading-[24px] text-[#464647]">
234
+ Desde
235
+ </span>
236
+ </div>
237
+
238
+ <div
239
+ className="col-start-2 row-start-1 flex items-center justify-center absolute"
240
+ style={{ top: "-22px", right: "30px" }}
241
+ >
242
+ {discountValue != null && (
243
+ <span className="rounded-[100px] bg-[#ff5964] px-[6px] text-[12px] bold-text leading-[20px] text-white">
244
+ {discountValue}% OFF
245
+ </span>
246
+ )}
247
+ </div>
248
+
249
+ <div
250
+ className="col-start-2 row-start-2 flex items-center justify-center "
251
+ style={{ textAlign: "center" }}
252
+ >
253
+ <span className="text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] line-through">
254
+ {formatPrice(discountSeat.originalPrice)}
255
+ </span>
256
+ </div>
257
+
258
+ <div className="col-start-2 row-start-3 flex h-[30px] items-end justify-start">
259
+ <span
260
+ className="flex items-center gap-[6px] text-[22px] bold-text leading-[30px]"
261
+ style={{ color: isSoldOut ? "#c0c0c0" : "#ff5964" }}
262
+ >
263
+ {/* <span className="text-[18px] leading-[24px]">🔥</span> */}
264
+ {renderIcon("fireIcon", "16px")}
265
+ {formatPrice(discountSeat.discountedPrice)}
266
+ </span>
267
+ </div>
268
+ </div>
269
+ );
270
+ }
271
+
228
272
  return (
229
273
  <div
230
274
  className="relative flex gap-[10px] text-[13.33px] justify-between min-h-[2.2rem]"
@@ -245,17 +289,6 @@ function SeatSection({
245
289
  gap: "10px",
246
290
  }}
247
291
  >
248
- {/* {isPeru && (
249
- <span className="text-[13.33px]" style={strikethroughStyle}>
250
- {formatPrice(1000)}
251
- </span>
252
- )} */}
253
-
254
- {hasDiscount && !isPeru && (
255
- <span className="text-[13.33px]" style={strikethroughStyle}>
256
- {formatPrice(highestOriginalPrice)}
257
- </span>
258
- )}
259
292
  {renderSeatPrices()}
260
293
  </div>
261
294
  </div>
@@ -36,6 +36,30 @@ function ExpandedDropdownMobile({
36
36
  className="flex flex-col gap-[8px] text-[11px] min-[420px]:text-[12px] text-[#464647]"
37
37
  style={{ lineHeight: 1.6 }}
38
38
  >
39
+ {isPeru ? null : isChangeTicket ? (
40
+ <div className="flex gap-[6px]">
41
+ <span style={{ marginTop: "2px" }}>•</span>
42
+ <span>
43
+ <span className="bold-text">Pasaje flexible:</span> Tu pasaje
44
+ puede ser cambiado de manera online{" "}
45
+ <span className="bold-text">
46
+ hasta {serviceItem?.change_ticket_hours || 6} horas antes
47
+ </span>{" "}
48
+ de la salida del bus. El monto será reembolsado a tu billetera
49
+ kupospay.
50
+ </span>
51
+ </div>
52
+ ) : (
53
+ <div className="flex gap-[8px] ">
54
+ <span style={{ marginTop: "2px" }}>•</span>
55
+ <span>
56
+ <span>
57
+ <span className="bold-text">Pasaje flexible:</span> Esta empresa
58
+ no permite cambios de pasajes
59
+ </span>
60
+ </span>
61
+ </div>
62
+ )}
39
63
  {petSeatInfo && Object.keys(petSeatInfo).length > 0 ? (
40
64
  <div className="flex items-center">
41
65
  <div className={`relative group cursor-default `}>
@@ -72,30 +96,6 @@ function ExpandedDropdownMobile({
72
96
  salida del bus.
73
97
  </span>
74
98
  </div>
75
- {isPeru ? null : isChangeTicket ? (
76
- <div className="flex gap-[6px]">
77
- <span style={{ marginTop: "2px" }}>•</span>
78
- <span>
79
- <span className="bold-text">Políticas de cambios:</span> Tu pasaje
80
- puede ser cambiado de manera online{" "}
81
- <span className="bold-text">
82
- hasta {serviceItem?.change_ticket_hours || 6} horas antes
83
- </span>{" "}
84
- de la salida del bus. El monto será reembolsado a tu billetera
85
- kupospay.
86
- </span>
87
- </div>
88
- ) : (
89
- <div className="flex gap-[8px] ">
90
- <span style={{ marginTop: "2px" }}>•</span>
91
- <span>
92
- <span>
93
- <span className="bold-text">Política de cambios:</span> Esta
94
- empresa no permite cambios de pasajes
95
- </span>
96
- </span>
97
- </div>
98
- )}
99
99
  </div>
100
100
  </div>
101
101
  );
@@ -137,14 +137,6 @@ function SeatSectionMobile({
137
137
  );
138
138
  };
139
139
 
140
- const getHighestFare = (): number => {
141
- return (
142
- seatTypesData
143
- ?.filter((item) => !EXCEPTIONS.includes(item.label))
144
- ?.reduce((max, item) => (item.fare > max ? item.fare : max), 0) || 0
145
- );
146
- };
147
-
148
140
  const renderPeruSeats = () => {
149
141
  const lowestFare = getLowestFare();
150
142
  if (lowestFare === null) return null;
@@ -242,42 +234,86 @@ function SeatSectionMobile({
242
234
  ...commonService.calculateDiscountedPrice(seat.fare, serviceItem),
243
235
  }));
244
236
 
245
- const highestOriginalPrice = Math.max(
246
- ...(discountedSeats?.map((s) => s.originalPrice) || [0]),
247
- );
248
237
  const hasDiscount = discountedSeats?.some(
249
238
  (s) => s.originalPrice !== s.discountedPrice,
250
239
  );
240
+ const discountSeat = discountedSeats
241
+ ?.filter((seat) => !EXCEPTIONS.includes(seat.label))
242
+ ?.sort((a, b) => a.discountedPrice - b.discountedPrice)[0];
243
+ const discountValue =
244
+ serviceItem?.discount_type === "percentage" &&
245
+ typeof serviceItem?.discount_value === "number"
246
+ ? Math.round(serviceItem.discount_value)
247
+ : null;
251
248
 
252
249
  return (
253
250
  <div className="content-center relative" style={{ width: "40%" }}>
254
- {!isPeru && hasDiscount && (
255
- <div className="absolute -top-[16px] right-[0] flex justify-end">
251
+ {hasDiscount && discountSeat ? (
252
+ <div className="relative grid grid-cols-[auto_auto] justify-between gap-x-[8px]">
253
+ {discountValue != null && (
254
+ <div className="absolute -top-[18px] right-[0px]">
255
+ <span
256
+ className="rounded-[100px] px-[8px] text-[12px] bold-text leading-[20px] text-[#fff]"
257
+ style={{ backgroundColor: seatPriceColor }}
258
+ >
259
+ {discountValue}% OFF
260
+ </span>
261
+ </div>
262
+ )}
263
+
264
+ <span className="min-[420]:text-[13px] text-[12px] leading-[20px] text-[#c2c2c2]">
265
+ Antes
266
+ </span>
267
+ <span className="min-[420]:text-[13px] text-[12px] leading-[20px] text-[#9f9f9f] line-through text-right">
268
+ {commonService.currency(discountSeat.originalPrice, currencySign)}
269
+ </span>
270
+
271
+ <span
272
+ className="min-[420]:text-[13px] text-[12px] leading-[24px]"
273
+ style={{ color: isSoldOut ? "#bbb" : "#464647" }}
274
+ >
275
+ Desde
276
+ </span>
256
277
  <span
257
- className="min-[420]:text-[13px] text-[12px] line-through"
258
- style={{ color: "#bbb" }}
278
+ className="flex items-center justify-end gap-[4px] text-[14px] bold-text leading-[24px]"
279
+ style={{ color: isSoldOut ? "#bbb" : "#ff5964" }}
259
280
  >
260
- {commonService.currency(highestOriginalPrice, currencySign)}
281
+ {serviceItem?.icons?.fireIcon ? (
282
+ <img
283
+ src={serviceItem.icons.fireIcon}
284
+ alt="discount"
285
+ className="h-[16px] w-[16px] object-contain"
286
+ style={{ filter: isSoldOut ? "grayscale" : "" }}
287
+ />
288
+ ) : null}
289
+ {commonService.currency(discountSeat.discountedPrice, currencySign)}
261
290
  </span>
262
- </div>
263
- )}
264
- <div
265
- className="flex flex-col justify-between h-[2.5rem] "
266
- style={{
267
- gap: isSoldOut ? "0px" : "5px",
268
- justifyContent: hasMultipleTypes ? "space-between" : "center",
269
- }}
270
- >
271
- {renderSeats()}
272
291
 
273
- {isSoldOut ? (
274
- <div className="flex justify-end">
275
- <span className="min-[420]:text-[13px] text-[12px] text-[#ccc]">
292
+ {isSoldOut ? (
293
+ <span className="col-span-2 min-[420]:text-[13px] text-right text-[12px] text-[#ccc]">
276
294
  Agotado
277
295
  </span>
278
- </div>
279
- ) : null}
280
- </div>
296
+ ) : null}
297
+ </div>
298
+ ) : (
299
+ <div
300
+ className="flex flex-col justify-between h-[2.5rem] "
301
+ style={{
302
+ gap: isSoldOut ? "0px" : "5px",
303
+ justifyContent: hasMultipleTypes ? "space-between" : "center",
304
+ }}
305
+ >
306
+ {renderSeats()}
307
+
308
+ {isSoldOut ? (
309
+ <div className="flex justify-end">
310
+ <span className="min-[420]:text-[13px] text-[12px] text-[#ccc]">
311
+ Agotado
312
+ </span>
313
+ </div>
314
+ ) : null}
315
+ </div>
316
+ )}
281
317
  </div>
282
318
  );
283
319
  }