kupos-ui-components-lib 9.8.3 → 9.8.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.
@@ -17,22 +17,53 @@ const HARDCODED_OPERATORS = [
17
17
  },
18
18
  {
19
19
  logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Pullman_Bus_logo.svg/320px-Pullman_Bus_logo.svg.png",
20
- name: "Pullmanbus",
20
+ name: "Turbus",
21
21
  time: "8:00 am",
22
22
  seatsAvailable: "5 disponibles",
23
23
  },
24
24
  {
25
25
  logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
26
- name: "Expreso Santa C...",
26
+ name: "Turbus",
27
27
  time: "9:00 am",
28
28
  seatsAvailable: "3 disponibles",
29
29
  },
30
30
  ];
31
- const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity = 1, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot = TIME_SLOTS[0], onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, }) => {
32
- var _a, _b, _c, _d, _e;
33
- const operators = ((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operators) === null || _a === void 0 ? void 0 : _a.length) > 0
34
- ? serviceItem.operators
35
- : HARDCODED_OPERATORS;
31
+ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity = 1, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot = TIME_SLOTS[0], onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData = undefined, }) => {
32
+ var _a, _b, _c, _d, _e, _f;
33
+ // Use wow_deal data if available, otherwise fall back to serviceItem operators or hardcoded
34
+ const operators = ((_a = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.operators) === null || _a === void 0 ? void 0 : _a.length) > 0
35
+ ? wowDealData.operators.map((op) => ({
36
+ logo: op.operator_logo_url,
37
+ name: op.operator_name,
38
+ time: op.starting_departure,
39
+ seatsAvailable: `${op.available_seats} disponibles`,
40
+ }))
41
+ : ((_b = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operators) === null || _b === void 0 ? void 0 : _b.length) > 0
42
+ ? serviceItem.operators
43
+ : HARDCODED_OPERATORS;
44
+ // Use wow_deal pricing if available
45
+ const finalPrice = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.final_price) || 4000;
46
+ const originalPrice = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.original_price) || 10000;
47
+ const savingsPercent = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.savings_percent) || 60;
48
+ const operatorsCompetingCount = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.operators_competing_count) || 3;
49
+ const maxSeatsPerBooking = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.max_seats_per_booking) || 4;
50
+ const expiresAt = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.expires_at;
51
+ const isPostPaymentAssignment = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.is_post_payment_assignment;
52
+ const dealWindowFrom = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.deal_window_from) || "07:00";
53
+ const dealWindowTo = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.deal_window_to) || "10:00";
54
+ const travelDate = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.travel_date;
55
+ // Calculate countdown seconds from expires_at ISO timestamp
56
+ const getCountdownSeconds = () => {
57
+ if (!expiresAt)
58
+ return 599;
59
+ const expires = new Date(expiresAt).getTime();
60
+ const now = Date.now();
61
+ const seconds = Math.max(0, Math.floor((expires - now) / 1000));
62
+ return seconds;
63
+ };
64
+ // Generate time slot from deal window
65
+ const dynamicTimeSlot = `Entre ${dealWindowFrom} y ${dealWindowTo}`;
66
+ const displayTimeSlot = selectedTimeSlot || dynamicTimeSlot;
36
67
  const isItemExpanded = serviceItem.id === isFeatureDropDownExpand ||
37
68
  isFeatureDropDownExpand === true;
38
69
  const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
@@ -41,12 +72,14 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
41
72
  {
42
73
  icon: "flexible",
43
74
  name: "Salida flexible",
44
- text: "Viajas en un horario entre las 07:00 y las 10:00 AM del día elegido.",
75
+ text: `Viajas en un horario entre las ${dealWindowFrom} y las ${dealWindowTo} del día elegido.`,
45
76
  },
46
77
  {
47
78
  icon: "bus",
48
79
  name: "Empresa asignada",
49
- text: "Una de las empresas confirmará tu viaje al instante tras el pago.",
80
+ text: isPostPaymentAssignment
81
+ ? "Empresa y hora a confirmar luego del pago."
82
+ : "Una de las empresas confirmará tu viaje al instante tras el pago.",
50
83
  },
51
84
  {
52
85
  icon: "price",
@@ -93,43 +126,36 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
93
126
  React.createElement("span", null, "Salida flexible"),
94
127
  React.createElement("div", { className: "bold-text font-[9px]", style: {
95
128
  backgroundColor: "#FF5C60",
96
- padding: "1px 8px",
97
- borderRadius: "4px",
129
+ padding: "4px 8px",
130
+ borderRadius: "10px",
98
131
  color: "#fff",
99
132
  animation: "pulse-zoom 2s ease-in-out infinite",
100
133
  whiteSpace: "nowrap",
134
+ fontSize: "10px",
101
135
  } },
102
- React.createElement("span", null, "AHORRAS 60%")))),
136
+ React.createElement("span", null,
137
+ "AHORRAS ",
138
+ savingsPercent,
139
+ "%")))),
103
140
  React.createElement("div", { id: `service-card-${serviceItem.id}`, className: "bg-[#0C1421] text-white mx-auto relative rounded-[14px] p-[14px] text-[13.33px]" },
104
141
  React.createElement("div", { className: "flex flex-col gap-[10px]" },
105
142
  React.createElement("div", { className: " text-[white]" },
106
143
  React.createElement("div", { className: "flex flex-col gap-[10px] relative" },
107
144
  React.createElement("div", { className: "flex items-center gap-[6px]" },
108
- React.createElement("img", { src: (_b = serviceItem.icons) === null || _b === void 0 ? void 0 : _b.whiteOrigin, alt: "origin", className: `w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}` }),
109
- React.createElement("span", { className: "text-[13px] bold-text" }, cityOrigin === null || cityOrigin === void 0 ? void 0 :
110
- cityOrigin.label.split(",")[0],
111
- React.createElement("span", { className: "mx-[10px]" }, "\u2192"), cityDestination === null || cityDestination === void 0 ? void 0 :
112
- cityDestination.label.split(",")[0])),
113
- React.createElement("div", { style: {
114
- width: "1px",
115
- flex: 1,
116
- backgroundColor: "#fff",
117
- margin: "3px 0",
118
- minHeight: "8px",
119
- position: "absolute",
120
- top: "13px",
121
- left: "7px",
122
- } }),
145
+ React.createElement("img", { src: (_c = serviceItem.icons) === null || _c === void 0 ? void 0 : _c.whiteOrigin, alt: "origin", className: `w-[13px] h-[13px] shrink-0 ${isSoldOut ? "grayscale" : ""}` }),
146
+ React.createElement("span", { className: "text-[14px] bold-text" }, cityOrigin === null || cityOrigin === void 0 ? void 0 : cityOrigin.label.split(",")[0]),
147
+ React.createElement("span", { className: "mx-[6px] text-[14px] bold-text" }, "\u2192"),
148
+ React.createElement("img", { src: (_d = serviceItem.icons) === null || _d === void 0 ? void 0 : _d.whiteDestination, alt: "destination", className: `w-[13px] h-[13px] shrink-0 ${isSoldOut ? "grayscale" : ""}`, style: { opacity: isSoldOut ? 0.5 : 1 } }),
149
+ React.createElement("span", { className: "text-[14px] bold-text" }, cityDestination === null || cityDestination === void 0 ? void 0 : cityDestination.label.split(",")[0])),
123
150
  React.createElement("div", { className: "flex items-center gap-[6px]" },
124
- React.createElement("img", { src: (_c = serviceItem.icons) === null || _c === void 0 ? void 0 : _c.whiteDestination, alt: "destination", className: `w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}`, style: { opacity: isSoldOut ? 0.5 : 1 } }),
125
151
  React.createElement("div", { className: "kupos-time-dd relative", tabIndex: 0, onBlur: (e) => {
126
152
  if (!e.currentTarget.contains(e.relatedTarget)) {
127
153
  onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
128
154
  }
129
155
  }, style: { outline: "none" } },
130
156
  React.createElement("button", { type: "button", onClick: () => onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(isThisTimeDropdownOpen ? null : serviceItem.id), className: "flex cursor-pointer select-none items-center gap-[6px] border-none bg-transparent p-0 bold-text text-[13px] text-[white]" },
131
- React.createElement("span", null, selectedTimeSlot),
132
- React.createElement("img", { src: (_d = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _d === void 0 ? void 0 : _d.downArrow, alt: "down arrow", className: `kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`, style: {
157
+ React.createElement("span", null, displayTimeSlot),
158
+ React.createElement("img", { src: (_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _e === void 0 ? void 0 : _e.downArrow, alt: "down arrow", className: `kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`, style: {
133
159
  width: "12px",
134
160
  height: "8px",
135
161
  filter: "brightness(0) invert(1)",
@@ -139,7 +165,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
139
165
  zIndex: 20,
140
166
  backgroundColor: "#fff",
141
167
  borderRadius: "14px",
142
- minWidth: "248px",
168
+ minWidth: "190px",
143
169
  boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
144
170
  overflow: "hidden",
145
171
  padding: "6px 0",
@@ -154,7 +180,8 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
154
180
  React.createElement("div", { className: "border-t border-[#363c48] my-[8px]" }),
155
181
  React.createElement("div", null,
156
182
  React.createElement("span", { className: "block w-full text-[14px] bold-text text-[white] mb-[10px]", style: { textAlign: "center" } },
157
- "3 operadores compitiendo ",
183
+ operatorsCompetingCount,
184
+ " operadores compitiendo ",
158
185
  React.createElement("br", null),
159
186
  "por tu compra"),
160
187
  React.createElement("div", { className: "flex gap-[8px] text-[white]", style: { width: "100%" } }, operators.map((op, idx) => (React.createElement("div", { key: idx, className: "flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]", style: {
@@ -165,14 +192,14 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
165
192
  backgroundColor: "#1a202e",
166
193
  padding: "14px 6px",
167
194
  } },
168
- React.createElement("img", { src: serviceItem.operator_details[0], alt: op.name, className: `h-[24px] max-w-full object-contain ${isSoldOut ? "grayscale" : ""}` }),
169
- React.createElement("span", { className: "text-[12px] truncate max-w-full text-center " }, serviceItem.operator_details[2]),
195
+ React.createElement("img", { src: op.logo, alt: op.name, onError: (e) => {
196
+ var _a;
197
+ e.currentTarget.src = ((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_details) === null || _a === void 0 ? void 0 : _a[0]) || "/images/service-list/bus-icon.svg";
198
+ }, className: `h-[24px] max-w-full object-contain ${isSoldOut ? "grayscale" : ""}` }),
199
+ React.createElement("span", { className: "text-[12px] truncate max-w-full text-center " }, op.name),
170
200
  React.createElement("span", { className: "text-[11px] whitespace-nowrap" }, op === null || op === void 0 ? void 0 : op.seatsAvailable))))),
171
201
  React.createElement("div", { className: "flex w-full items-center justify-center gap-[6px] text-[12px] mt-[12px]", style: {
172
- border: "1px solid #363c48",
173
- backgroundColor: "#1a202e",
174
202
  padding: "4px 14px",
175
- borderRadius: "24px",
176
203
  } },
177
204
  React.createElement(LottiePlayer, { animationData: serviceItem.icons.personsAnim, width: "18px", height: "18px" }),
178
205
  React.createElement("span", null,
@@ -184,7 +211,8 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
184
211
  } }),
185
212
  " "),
186
213
  " ",
187
- React.createElement("span", { className: "text-[white]" }, "viendo |"),
214
+ React.createElement("span", { style: { color: "#FF5C60" } }, "viendo"),
215
+ React.createElement("span", { className: "text-[white]" }, " |"),
188
216
  " ",
189
217
  React.createElement("span", { className: "bold-text text-[white]", ref: (node) => commonService.startComprandoCount(node, 4, 16), style: { fontVariantNumeric: "tabular-nums" } }),
190
218
  " ",
@@ -197,11 +225,11 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
197
225
  padding: "4px 14px",
198
226
  borderRadius: "14px",
199
227
  } },
200
- React.createElement("button", { type: "button", "aria-label": "Disminuir pasajes", disabled: !canDecreaseTicketQuantity, onClick: () => onDecreaseTicketQuantity === null || onDecreaseTicketQuantity === void 0 ? void 0 : onDecreaseTicketQuantity(serviceItem), className: `flex h-[34px] w-[34px] items-center justify-center rounded-full border-none text-[25px] leading-none text-[white] ${canDecreaseTicketQuantity
228
+ React.createElement("button", { type: "button", "aria-label": "Disminuir pasajes", disabled: !canDecreaseTicketQuantity, onClick: () => onDecreaseTicketQuantity === null || onDecreaseTicketQuantity === void 0 ? void 0 : onDecreaseTicketQuantity(serviceItem), className: `flex h-[26px] w-[26px] items-center justify-center rounded-full border-none text-[16px] leading-none text-[white] ${canDecreaseTicketQuantity
201
229
  ? "cursor-pointer bg-[#2d374d]"
202
230
  : "cursor-not-allowed bg-[#222b3d] opacity-50"}` }, "-"),
203
- React.createElement("span", { className: "bold-text text-[20px] text-[white]" }, ticketQuantity),
204
- React.createElement("button", { type: "button", "aria-label": "Aumentar pasajes", onClick: () => onIncreaseTicketQuantity === null || onIncreaseTicketQuantity === void 0 ? void 0 : onIncreaseTicketQuantity(serviceItem), className: "flex h-[34px] w-[34px] cursor-pointer items-center justify-center rounded-full border-none bg-[#2d374d] text-[25px] leading-none text-[white]" }, "+"))),
231
+ React.createElement("span", { className: "bold-text text-[14px] text-[white]" }, ticketQuantity),
232
+ React.createElement("button", { type: "button", "aria-label": "Aumentar pasajes", onClick: () => onIncreaseTicketQuantity === null || onIncreaseTicketQuantity === void 0 ? void 0 : onIncreaseTicketQuantity(serviceItem), className: "flex h-[26px] w-[26px] cursor-pointer items-center justify-center rounded-full border-none bg-[#2d374d] text-[16px] leading-none text-[white]" }, "+"))),
205
233
  React.createElement("div", { className: "mt-[10px] flex justify-between items-center rounded-[14px]", style: {
206
234
  // height: "80px",
207
235
  border: "1px solid #363c48",
@@ -209,8 +237,9 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
209
237
  padding: "14px 10px",
210
238
  } },
211
239
  React.createElement("div", { className: "flex flex-col" },
212
- React.createElement("span", { className: "text-[20px] font-normal leading-[20px] text-[#9f9f9f] relative", style: { position: "relative" } },
213
- "$10.000",
240
+ React.createElement("span", { className: "text-[18px] font-normal leading-[20px] text-[#9f9f9f] relative", style: { position: "relative" } },
241
+ "$",
242
+ originalPrice.toLocaleString(),
214
243
  React.createElement("span", { style: {
215
244
  position: "absolute",
216
245
  left: "-2px",
@@ -221,24 +250,26 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
221
250
  transform: "rotate(-10deg)",
222
251
  transformOrigin: "center",
223
252
  } })),
224
- React.createElement("span", { className: "text-[white] bold-text text-[28px] leading-none mt-[4px]" }, `$${(4000 * ticketQuantity).toLocaleString()}`)),
225
- React.createElement("span", { className: "text-[#FF8F45] bold-text text-[26px] leading-tight", style: {
253
+ React.createElement("span", { className: "text-[white] bold-text text-[24px] leading-none mt-[4px]" }, `$${(finalPrice * ticketQuantity).toLocaleString()}`)),
254
+ React.createElement("span", { className: "text-[#FF8F45] bold-text text-[22px] leading-tight", style: {
226
255
  animation: "pulse-zoom 2s ease-in-out infinite",
227
256
  whiteSpace: "nowrap",
228
- } }, "60% OFF")),
257
+ } },
258
+ savingsPercent,
259
+ "% OFF")),
229
260
  React.createElement("button", { type: "button", onClick: onBookButtonPress, className: "flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-[white] bold-text text-[13px] mt-[10px] justify-center border-none cursor-pointer", style: {
230
261
  backgroundColor: "#FF5C60",
231
- animation: "pulse-zoom 2s ease-in-out infinite",
232
262
  whiteSpace: "nowrap",
233
263
  width: "100%",
234
264
  } },
235
- React.createElement(LottiePlayer, { animationData: serviceItem.icons.thunderAnim, width: "18px", height: "18px" }),
265
+ React.createElement(LottiePlayer, { animationData: serviceItem.icons.thunderAnim, width: "16px", height: "16px" }),
236
266
  React.createElement("span", { className: "whitespace-nowrap" }, "\u00A1Lo quiero!")),
237
267
  React.createElement("div", { className: "flex justify-end mt-[10px]", onClick: onToggleExpand },
238
- React.createElement("img", { src: (_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _e === void 0 ? void 0 : _e.downArrow, alt: "down arrow", className: `transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`, style: {
268
+ React.createElement("img", { src: (_f = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _f === void 0 ? void 0 : _f.downArrow, alt: "down arrow", className: `transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`, style: {
239
269
  width: "14px",
240
270
  height: "8px",
241
271
  filter: "brightness(0) invert(1)",
272
+ marginRight: "2px",
242
273
  } }))))),
243
274
  isItemExpanded && (React.createElement("div", { className: "flex flex-col gap-[10px]", style: {
244
275
  opacity: isItemExpanded ? 1 : 0,
@@ -261,7 +292,7 @@ const FeatureServiceUiMobile = ({ serviceItem, showTopLabel, colors, isSoldOut,
261
292
  " ",
262
293
  React.createElement("span", { className: "text-[white]" }, "t\u00E9rmina en"),
263
294
  " ",
264
- React.createElement("span", { className: "bold-text text-end", ref: (node) => commonService.startCountdown(node, 599), style: {
295
+ React.createElement("span", { className: "bold-text text-end", ref: (node) => commonService.startCountdown(node, getCountdownSeconds()), style: {
265
296
  fontVariantNumeric: "tabular-nums",
266
297
  display: "inline-block",
267
298
  color: "#fff",
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- declare const FeatureServiceUi: ({ serviceItem, showTopLabel, isSoldOut, getAnimationIcon, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, }: {
2
+ declare const FeatureServiceUi: ({ serviceItem, showTopLabel, isSoldOut, getAnimationIcon, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData, }: {
3
3
  serviceItem: any;
4
4
  showTopLabel: any;
5
5
  isSoldOut: any;
@@ -18,5 +18,6 @@ declare const FeatureServiceUi: ({ serviceItem, showTopLabel, isSoldOut, getAnim
18
18
  onTimeSlotChange: any;
19
19
  isTimeDropdownOpen: any;
20
20
  onTimeDropdownToggle: any;
21
+ wowDealData?: any;
21
22
  }) => React.JSX.Element;
22
23
  export default FeatureServiceUi;
@@ -16,22 +16,53 @@ const HARDCODED_OPERATORS = [
16
16
  },
17
17
  {
18
18
  logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Pullman_Bus_logo.svg/320px-Pullman_Bus_logo.svg.png",
19
- name: "Pullmanbus",
19
+ name: "Turbus",
20
20
  time: "8:00 am",
21
21
  seatsAvailable: "5 disponibles",
22
22
  },
23
23
  {
24
24
  logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
25
- name: "Expreso Santa C...",
25
+ name: "Turbus",
26
26
  time: "9:00 am",
27
27
  seatsAvailable: "3 disponibles",
28
28
  },
29
29
  ];
30
- const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIcon, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity = 1, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, }) => {
31
- var _a, _b, _c, _d, _e;
32
- const operators = ((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operators) === null || _a === void 0 ? void 0 : _a.length) > 0
33
- ? serviceItem.operators
34
- : HARDCODED_OPERATORS;
30
+ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIcon, cityOrigin, cityDestination, renderIcon, viewersConfig, isFeatureDropDownExpand, onToggleExpand, ticketQuantity = 1, onIncreaseTicketQuantity, onDecreaseTicketQuantity, onBookButtonPress, selectedTimeSlot, onTimeSlotChange, isTimeDropdownOpen, onTimeDropdownToggle, wowDealData = undefined, }) => {
31
+ var _a, _b, _c, _d, _e, _f;
32
+ // Use wow_deal data if available, otherwise fall back to serviceItem operators or hardcoded
33
+ const operators = ((_a = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.operators) === null || _a === void 0 ? void 0 : _a.length) > 0
34
+ ? wowDealData.operators.map((op) => ({
35
+ logo: op.operator_logo_url,
36
+ name: op.operator_name,
37
+ time: op.starting_departure,
38
+ seatsAvailable: `${op.available_seats} disponibles`,
39
+ }))
40
+ : ((_b = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operators) === null || _b === void 0 ? void 0 : _b.length) > 0
41
+ ? serviceItem.operators
42
+ : HARDCODED_OPERATORS;
43
+ // Use wow_deal pricing if available
44
+ const finalPrice = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.final_price) || 4000;
45
+ const originalPrice = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.original_price) || 10000;
46
+ const savingsPercent = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.savings_percent) || 60;
47
+ const operatorsCompetingCount = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.operators_competing_count) || 3;
48
+ const maxSeatsPerBooking = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.max_seats_per_booking) || 4;
49
+ const expiresAt = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.expires_at;
50
+ const isPostPaymentAssignment = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.is_post_payment_assignment;
51
+ const dealWindowFrom = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.deal_window_from) || "07:00";
52
+ const dealWindowTo = (wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.deal_window_to) || "10:00";
53
+ const travelDate = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.travel_date;
54
+ // Calculate countdown seconds from expires_at ISO timestamp
55
+ const getCountdownSeconds = () => {
56
+ if (!expiresAt)
57
+ return 599;
58
+ const expires = new Date(expiresAt).getTime();
59
+ const now = Date.now();
60
+ const seconds = Math.max(0, Math.floor((expires - now) / 1000));
61
+ return seconds;
62
+ };
63
+ // Generate time slot from deal window
64
+ const dynamicTimeSlot = `Entre ${dealWindowFrom} y ${dealWindowTo}`;
65
+ const displayTimeSlot = selectedTimeSlot || dynamicTimeSlot;
35
66
  const isItemExpanded = serviceItem.id === isFeatureDropDownExpand ||
36
67
  isFeatureDropDownExpand === true;
37
68
  const isThisTimeDropdownOpen = isTimeDropdownOpen === serviceItem.id;
@@ -40,12 +71,14 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
40
71
  {
41
72
  icon: "flexible",
42
73
  name: "Salida flexible",
43
- text: "Viajas en un horario entre las 07:00 y las 10:00 AM del día elegido.",
74
+ text: `Viajas en un horario entre las ${dealWindowFrom} y las ${dealWindowTo} del día elegido.`,
44
75
  },
45
76
  {
46
77
  icon: "bus",
47
78
  name: "Empresa asignada",
48
- text: "Una de las empresas confirmará tu viaje al instante tras el pago.",
79
+ text: isPostPaymentAssignment
80
+ ? "Empresa y hora a confirmar luego del pago."
81
+ : "Una de las empresas confirmará tu viaje al instante tras el pago.",
49
82
  },
50
83
  {
51
84
  icon: "price",
@@ -90,13 +123,17 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
90
123
  React.createElement("span", null, "Salida flexible"),
91
124
  React.createElement("div", { className: "bold-text font-[9px]", style: {
92
125
  backgroundColor: "#FF5C60",
93
- padding: "1px 8px",
94
- borderRadius: "4px",
126
+ padding: "5px 8px",
127
+ borderRadius: "10px",
95
128
  color: "#fff",
96
129
  animation: "pulse-zoom 2s ease-in-out infinite",
97
130
  whiteSpace: "nowrap",
131
+ fontSize: "12px",
98
132
  } },
99
- React.createElement("span", null, "AHORRAS 60%"))),
133
+ React.createElement("span", null,
134
+ "AHORRAS ",
135
+ savingsPercent,
136
+ "%"))),
100
137
  React.createElement("div", { className: "flex items-center" },
101
138
  React.createElement("div", { className: "mb-[2px]" },
102
139
  React.createElement(LottiePlayer
@@ -105,9 +142,9 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
105
142
  // animationData={serviceItem.icons.flexibleAnim}
106
143
  animationData: getAnimationIcon("flameAnimation"), width: "18px", height: "18px" })),
107
144
  React.createElement("span", { className: "bold-text" }, "Remate"),
108
- " t\u00E9rmina en",
145
+ "\u00A0t\u00E9rmina en",
109
146
  " ",
110
- React.createElement("span", { className: "bold-text text-end", ref: (node) => commonService.startCountdown(node, 599), style: {
147
+ React.createElement("span", { className: "bold-text text-end", ref: (node) => commonService.startCountdown(node, getCountdownSeconds()), style: {
111
148
  fontVariantNumeric: "tabular-nums",
112
149
  display: "inline-block",
113
150
  color: "#FF5C60",
@@ -115,24 +152,24 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
115
152
  } }))),
116
153
  React.createElement("div", { id: `service-card-${serviceItem.id}`, className: "bg-[#0C1421] text-white mx-auto relative rounded-[14px] p-[14px] text-[13.33px]" },
117
154
  React.createElement("div", { className: "grid grid-cols-[25%_48%_27%] items-stretch" },
118
- React.createElement("div", { className: "flex flex-col justify-between gap-[20px] my-[14px] pr-[22px]" },
155
+ React.createElement("div", { className: "flex flex-col justify-between gap-[20px] mb-[16px] pr-[22px]" },
119
156
  React.createElement("div", { className: "flex flex-col gap-[8px]" },
120
157
  React.createElement("div", { className: "flex items-center gap-[8px]" },
121
- React.createElement("img", { src: (_b = serviceItem.icons) === null || _b === void 0 ? void 0 : _b.whiteOrigin, alt: "origin", className: `w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}` }),
158
+ React.createElement("img", { src: (_c = serviceItem.icons) === null || _c === void 0 ? void 0 : _c.whiteOrigin, alt: "origin", className: `w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}` }),
122
159
  React.createElement("span", { className: "text-[13px] bold-text" }, cityOrigin === null || cityOrigin === void 0 ? void 0 : cityOrigin.label.split(",")[0])),
123
160
  React.createElement("div", { className: "flex items-center gap-[8px]" },
124
- React.createElement("img", { src: (_c = serviceItem.icons) === null || _c === void 0 ? void 0 : _c.whiteDestination, alt: "destination", className: `w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}`, style: { opacity: isSoldOut ? 0.5 : 1 } }),
161
+ React.createElement("img", { src: (_d = serviceItem.icons) === null || _d === void 0 ? void 0 : _d.whiteDestination, alt: "destination", className: `w-[14px] h-[14px] shrink-0 ${isSoldOut ? "grayscale" : ""}`, style: { opacity: isSoldOut ? 0.5 : 1 } }),
125
162
  React.createElement("span", { className: "text-[13px] bold-text" }, cityDestination === null || cityDestination === void 0 ? void 0 : cityDestination.label.split(",")[0]))),
126
163
  React.createElement("div", { className: "flex flex-col gap-[8px]" },
127
- React.createElement("div", { className: "text-[12px] bold-text" }, "Viernes 23 de mayo"),
164
+ React.createElement("div", { className: "text-[12px] bold-text" }, travelDate ? new Date(travelDate).toLocaleDateString('es-CL', { weekday: 'long', day: 'numeric', month: 'long' }) : 'Viernes 23 de mayo'),
128
165
  React.createElement("div", { className: "kupos-time-dd relative", tabIndex: 0, onBlur: (e) => {
129
166
  if (!e.currentTarget.contains(e.relatedTarget)) {
130
167
  onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(null);
131
168
  }
132
169
  }, style: { outline: "none" } },
133
170
  React.createElement("button", { type: "button", onClick: () => onTimeDropdownToggle === null || onTimeDropdownToggle === void 0 ? void 0 : onTimeDropdownToggle(isThisTimeDropdownOpen ? null : serviceItem.id), className: "flex whitespace-nowrap cursor-pointer select-none items-center gap-[6px] border-none bg-transparent p-0 bold-text text-[12px] text-[white]" },
134
- React.createElement("span", null, selectedTimeSlot),
135
- React.createElement("img", { src: (_d = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _d === void 0 ? void 0 : _d.downArrow, alt: "down arrow", className: `kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`, style: {
171
+ React.createElement("span", null, displayTimeSlot),
172
+ React.createElement("img", { src: (_e = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.icons) === null || _e === void 0 ? void 0 : _e.downArrow, alt: "down arrow", className: `kupos-time-chevron transition-transform duration-200 ${isThisTimeDropdownOpen ? "rotate-180" : "rotate-0"}`, style: {
136
173
  width: "12px",
137
174
  height: "8px",
138
175
  filter: "brightness(0) invert(1)",
@@ -142,7 +179,7 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
142
179
  zIndex: 20,
143
180
  backgroundColor: "#fff",
144
181
  borderRadius: "14px",
145
- minWidth: "248px",
182
+ minWidth: "190px",
146
183
  boxShadow: "0 8px 32px rgba(0,0,0,0.28)",
147
184
  overflow: "hidden",
148
185
  padding: "6px 0",
@@ -168,25 +205,27 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
168
205
  React.createElement("div", { className: "min-w-0 px-[22px] flex flex-col items-center justify-between gap-[16px] py-[2px] border-r border-[#363c48] border-l border-[#363c48]" },
169
206
  React.createElement("div", { className: "text-center" },
170
207
  React.createElement("div", { className: "bold-text text-[14px]" },
171
- "3 operadores compitiendo",
208
+ operatorsCompetingCount,
209
+ " operadores compitiendo",
172
210
  React.createElement("br", null),
173
211
  " por tu compra")),
174
- React.createElement("div", { className: "grid w-full grid-cols-3 items-stretch gap-[14px] mb-[12px]" }, operators.map((op, idx) => (React.createElement("div", { key: idx, className: "flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]", style: {
212
+ React.createElement("div", { className: "grid w-full grid-cols-3 items-stretch gap-[14px] " }, operators.map((op, idx) => (React.createElement("div", { key: idx, className: "flex min-w-0 flex-col items-center justify-center gap-[8px] rounded-[8px]", style: {
175
213
  // height: "80px",
176
214
  border: "1px solid #363c48",
177
215
  backgroundColor: "#1a202e",
178
216
  padding: "14px 10px",
179
217
  } },
180
- React.createElement("img", { src: serviceItem.operator_details[0], alt: op.name, className: `h-[24px] max-w-full object-contain ${isSoldOut ? "grayscale" : ""}` }),
181
- React.createElement("span", { className: "text-[11px] truncate max-w-full text-center" }, serviceItem.operator_details[2]),
218
+ React.createElement("img", { src: op.logo, alt: op.name, onError: (e) => {
219
+ var _a;
220
+ e.currentTarget.src = ((_a = serviceItem === null || serviceItem === void 0 ? void 0 : serviceItem.operator_details) === null || _a === void 0 ? void 0 : _a[0]) || "/images/service-list/bus-icon.svg";
221
+ }, className: `h-[24px] max-w-full object-contain ${isSoldOut ? "grayscale" : ""}` }),
222
+ React.createElement("span", { className: "text-[11px] truncate max-w-full text-center" }, op.name),
182
223
  React.createElement("div", { className: "bg-[#FF8F45] text-white text-[12px] font-bold px-[10px] py-[4px] rounded-[4px] bold-text whitespace-nowrap" },
183
224
  React.createElement("span", null, op === null || op === void 0 ? void 0 : op.time)),
184
225
  React.createElement("span", { className: "text-[10px] mt-[6px]" }, op === null || op === void 0 ? void 0 : op.seatsAvailable))))),
185
226
  React.createElement("div", { className: "flex w-full items-center justify-center gap-[6px] text-[12px]", style: {
186
- border: "1px solid #363c48",
187
- backgroundColor: "#1a202e",
188
227
  padding: "8px 14px",
189
- borderRadius: "24px",
228
+ marginBottom: "6px",
190
229
  } },
191
230
  React.createElement(LottiePlayer
192
231
  // animationData={serviceItem.icons.flexibleAnim}
@@ -195,14 +234,13 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
195
234
  animationData: getAnimationIcon("usersAnimation"), width: "18px", height: "18px" }),
196
235
  React.createElement("span", { className: "text-[13px]" },
197
236
  React.createElement("span", { className: "bold-text text-white" },
198
- " ",
199
237
  React.createElement("span", { className: "bold-text", ref: (node) => commonService.startViewerCount(node, viewersConfig), style: {
200
238
  fontVariantNumeric: "tabular-nums",
201
239
  color: "#FF5C60",
202
240
  } }),
203
241
  " "),
204
- " ",
205
- "viendo |",
242
+ React.createElement("span", { style: { color: "#FF5C60" } }, "viendo"),
243
+ " |",
206
244
  " ",
207
245
  React.createElement("span", { className: "bold-text", ref: (node) => commonService.startComprandoCount(node, 4, 16), style: { fontVariantNumeric: "tabular-nums" } }),
208
246
  " ",
@@ -214,9 +252,12 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
214
252
  React.createElement("span", { className: "text-[#FF8F45] bold-text text-[26px] leading-tight", style: {
215
253
  animation: "pulse-zoom 2s ease-in-out infinite",
216
254
  whiteSpace: "nowrap",
217
- } }, "60% OFF"),
255
+ } },
256
+ savingsPercent,
257
+ "% OFF"),
218
258
  React.createElement("span", { className: "text-[13.33px] font-normal leading-[20px] text-[#9f9f9f] relative", style: { position: "relative" } },
219
- "$10.000",
259
+ "$",
260
+ originalPrice.toLocaleString(),
220
261
  React.createElement("span", { style: {
221
262
  position: "absolute",
222
263
  left: "-2px",
@@ -227,33 +268,31 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
227
268
  transform: "rotate(-10deg)",
228
269
  transformOrigin: "center",
229
270
  } })),
230
- React.createElement("span", { className: "text-white bold-text text-[28px] leading-none" }, `$${(4000 * ticketQuantity).toLocaleString()}`)),
271
+ React.createElement("span", { className: "text-white bold-text text-[28px] leading-none" }, `$${(finalPrice * ticketQuantity).toLocaleString()}`)),
231
272
  React.createElement("div", { className: "mt-[4px] flex flex-col items-center gap-[8px]" },
232
273
  React.createElement("span", { className: "text-[12px] text-white" }, "\u00BFCu\u00E1ntos pasajes quieres?"),
233
- React.createElement("div", { className: "flex w-full items-center justify-between", style: {
274
+ React.createElement("div", { className: "flex w-full items-center justify-between rounded-[16px] ", style: {
234
275
  border: "1px solid #363c48",
235
276
  backgroundColor: "#1a202e",
236
- padding: "6px 14px",
237
- borderRadius: "14px",
277
+ padding: "4px 14px",
238
278
  } },
239
- React.createElement("button", { type: "button", "aria-label": "Disminuir pasajes", disabled: !canDecreaseTicketQuantity, onClick: () => onDecreaseTicketQuantity === null || onDecreaseTicketQuantity === void 0 ? void 0 : onDecreaseTicketQuantity(serviceItem), className: `flex h-[34px] w-[34px] items-center justify-center rounded-full border-none text-[25px] leading-none text-white ${canDecreaseTicketQuantity
279
+ React.createElement("button", { type: "button", "aria-label": "Disminuir pasajes", disabled: !canDecreaseTicketQuantity, onClick: () => onDecreaseTicketQuantity === null || onDecreaseTicketQuantity === void 0 ? void 0 : onDecreaseTicketQuantity(serviceItem), className: `flex h-[26px] w-[26px] items-center justify-center rounded-full border-none text-[16px] leading-none text-white ${canDecreaseTicketQuantity
240
280
  ? "cursor-pointer bg-[#2d374d]"
241
281
  : "cursor-not-allowed bg-[#222b3d] opacity-50"}` }, "-"),
242
- React.createElement("span", { className: "bold-text text-[20px] text-white" }, ticketQuantity),
243
- React.createElement("button", { type: "button", "aria-label": "Aumentar pasajes", onClick: () => onIncreaseTicketQuantity === null || onIncreaseTicketQuantity === void 0 ? void 0 : onIncreaseTicketQuantity(serviceItem), className: "flex h-[34px] w-[34px] cursor-pointer items-center justify-center rounded-full border-none bg-[#2d374d] text-[25px] leading-none text-white" }, "+"))),
244
- React.createElement("button", { type: "button", onClick: onBookButtonPress, className: "flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-white bold-text text-[13px] mt-[4px] justify-center border-none cursor-pointer", style: {
282
+ React.createElement("span", { className: "bold-text text-[14px] text-white" }, ticketQuantity),
283
+ React.createElement("button", { type: "button", "aria-label": "Aumentar pasajes", onClick: () => onIncreaseTicketQuantity === null || onIncreaseTicketQuantity === void 0 ? void 0 : onIncreaseTicketQuantity(serviceItem), className: "flex h-[26px] w-[26px] cursor-pointer items-center justify-center rounded-full border-none bg-[#2d374d] text-[16px] leading-none text-white" }, "+"))),
284
+ React.createElement("button", { type: "button", onClick: onBookButtonPress, className: "flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-white bold-text text-[13px] mt-[4px] justify-center border-none cursor-pointer text-center", style: {
245
285
  backgroundColor: "#FF5C60",
246
- animation: "pulse-zoom 2s ease-in-out infinite",
247
286
  whiteSpace: "nowrap",
248
287
  } },
249
288
  React.createElement(LottiePlayer
250
289
  // animationData={serviceItem.icons.flexibleAnim}
251
290
  , {
252
291
  // animationData={serviceItem.icons.flexibleAnim}
253
- animationData: getAnimationIcon("thunderAnimation"), width: "18px", height: "18px" }),
292
+ animationData: getAnimationIcon("thunderAnimation"), width: "16px", height: "16px" }),
254
293
  React.createElement("span", { className: "whitespace-nowrap" }, "\u00A1Lo quiero!"))),
255
294
  React.createElement("div", { className: `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`, onClick: onToggleExpand },
256
- React.createElement("img", { src: (_e = serviceItem.icons) === null || _e === void 0 ? void 0 : _e.downArrow, alt: "down arrow", style: {
295
+ React.createElement("img", { src: (_f = serviceItem.icons) === null || _f === void 0 ? void 0 : _f.downArrow, alt: "down arrow", style: {
257
296
  width: "14px",
258
297
  height: "8px",
259
298
  filter: "brightness(0) invert(1)",
@@ -296,10 +296,14 @@ const commonService = {
296
296
  startViewerCount: (node, viewersConfig) => {
297
297
  if (!node || !viewersConfig)
298
298
  return;
299
+ const { min, max, interval = 5000 } = viewersConfig;
300
+ const configKey = `${min}-${max}-${interval}`;
301
+ if (node.dataset.viewerId && node.dataset.viewerConfig === configKey) {
302
+ return;
303
+ }
299
304
  const prevId = node.dataset.viewerId;
300
305
  if (prevId)
301
306
  clearInterval(Number(prevId));
302
- const { min, max, interval = 5000 } = viewersConfig;
303
307
  const clamp = (v) => Math.min(max, Math.max(min, v));
304
308
  const initialValue = Math.floor(Math.random() * (max - min + 1)) + min;
305
309
  node.textContent = String(initialValue);
@@ -310,6 +314,7 @@ const commonService = {
310
314
  node.textContent = String(clamp(Math.round(next)));
311
315
  }, interval);
312
316
  node.dataset.viewerId = String(id);
317
+ node.dataset.viewerConfig = configKey;
313
318
  },
314
319
  startCountdown: (node, countdownSeconds = 599) => {
315
320
  if (!node)
@@ -339,6 +344,10 @@ const commonService = {
339
344
  startComprandoCount: (node, min = 4, max = 16) => {
340
345
  if (!node)
341
346
  return;
347
+ const configKey = `${min}-${max}`;
348
+ if (node.dataset.comprandoId && node.dataset.comprandoConfig === configKey) {
349
+ return;
350
+ }
342
351
  const prevId = node.dataset.comprandoId;
343
352
  if (prevId)
344
353
  clearInterval(Number(prevId));
@@ -355,6 +364,7 @@ const commonService = {
355
364
  node.textContent = String(next);
356
365
  }, 5000); // Update every 5 seconds
357
366
  node.dataset.comprandoId = String(id);
367
+ node.dataset.comprandoConfig = configKey;
358
368
  },
359
369
  };
360
370
  export default commonService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "9.8.3",
3
+ "version": "9.8.4",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -32,6 +32,9 @@ interface KuposUIComponentProps {
32
32
  orignLabel?: string;
33
33
  destinationLabel?: string;
34
34
  t?: (key: string) => string;
35
+ ticketQuantity?: number;
36
+ onIncreaseTicketQuantity?: (serviceItem: any) => void;
37
+ onDecreaseTicketQuantity?: (serviceItem: any) => void;
35
38
 
36
39
  // New ServiceItem props
37
40
  id?: string;