@eventlook/sdk 1.4.42 → 1.4.44

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.
Files changed (80) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/dist/cjs/form/Payment.js +0 -2
  3. package/dist/cjs/form/Payment.js.map +1 -1
  4. package/dist/cjs/form/PaymentOverviewBox.js +55 -77
  5. package/dist/cjs/form/PaymentOverviewBox.js.map +1 -1
  6. package/dist/cjs/form/TicketForm.js +3 -3
  7. package/dist/cjs/form/TicketForm.js.map +1 -1
  8. package/dist/cjs/form/TicketSelection.js +20 -4
  9. package/dist/cjs/form/TicketSelection.js.map +1 -1
  10. package/dist/cjs/form/TicketWithMerchandiseSelection.js +38 -4
  11. package/dist/cjs/form/TicketWithMerchandiseSelection.js.map +1 -1
  12. package/dist/cjs/form/payment/FeeBox.js +1 -1
  13. package/dist/cjs/form/payment/FeeBox.js.map +1 -1
  14. package/dist/cjs/locales/cs.js +3 -0
  15. package/dist/cjs/locales/cs.js.map +1 -1
  16. package/dist/cjs/locales/en.js +3 -0
  17. package/dist/cjs/locales/en.js.map +1 -1
  18. package/dist/cjs/locales/es.js +3 -0
  19. package/dist/cjs/locales/es.js.map +1 -1
  20. package/dist/cjs/locales/pl.js +3 -0
  21. package/dist/cjs/locales/pl.js.map +1 -1
  22. package/dist/cjs/locales/sk.js +3 -0
  23. package/dist/cjs/locales/sk.js.map +1 -1
  24. package/dist/cjs/locales/uk.js +3 -0
  25. package/dist/cjs/locales/uk.js.map +1 -1
  26. package/dist/cjs/modules/shopping-cart.js +10 -9
  27. package/dist/cjs/modules/shopping-cart.js.map +1 -1
  28. package/dist/esm/form/Payment.js +0 -2
  29. package/dist/esm/form/Payment.js.map +1 -1
  30. package/dist/esm/form/PaymentOverviewBox.js +57 -79
  31. package/dist/esm/form/PaymentOverviewBox.js.map +1 -1
  32. package/dist/esm/form/TicketForm.js +3 -3
  33. package/dist/esm/form/TicketForm.js.map +1 -1
  34. package/dist/esm/form/TicketSelection.js +21 -5
  35. package/dist/esm/form/TicketSelection.js.map +1 -1
  36. package/dist/esm/form/TicketWithMerchandiseSelection.js +39 -5
  37. package/dist/esm/form/TicketWithMerchandiseSelection.js.map +1 -1
  38. package/dist/esm/form/payment/FeeBox.js +1 -1
  39. package/dist/esm/form/payment/FeeBox.js.map +1 -1
  40. package/dist/esm/locales/cs.js +3 -0
  41. package/dist/esm/locales/cs.js.map +1 -1
  42. package/dist/esm/locales/en.js +3 -0
  43. package/dist/esm/locales/en.js.map +1 -1
  44. package/dist/esm/locales/es.js +3 -0
  45. package/dist/esm/locales/es.js.map +1 -1
  46. package/dist/esm/locales/pl.js +3 -0
  47. package/dist/esm/locales/pl.js.map +1 -1
  48. package/dist/esm/locales/sk.js +3 -0
  49. package/dist/esm/locales/sk.js.map +1 -1
  50. package/dist/esm/locales/uk.js +3 -0
  51. package/dist/esm/locales/uk.js.map +1 -1
  52. package/dist/esm/modules/shopping-cart.js +9 -8
  53. package/dist/esm/modules/shopping-cart.js.map +1 -1
  54. package/dist/types/locales/cs.d.ts +3 -0
  55. package/dist/types/locales/en.d.ts +3 -0
  56. package/dist/types/locales/es.d.ts +3 -0
  57. package/dist/types/locales/pl.d.ts +3 -0
  58. package/dist/types/locales/sk.d.ts +3 -0
  59. package/dist/types/locales/uk.d.ts +3 -0
  60. package/dist/types/modules/shopping-cart.d.ts +3 -5
  61. package/dist/types/utils/data/shopping-cart.d.ts +5 -0
  62. package/dist/types/utils/types/shopping-cart.type.d.ts +42 -40
  63. package/dist/types/utils/types/ticket.type.d.ts +1 -2
  64. package/package.json +1 -1
  65. package/src/form/Payment.tsx +0 -2
  66. package/src/form/PaymentOverviewBox.tsx +71 -88
  67. package/src/form/TicketForm.tsx +3 -3
  68. package/src/form/TicketSelection.tsx +22 -6
  69. package/src/form/TicketWithMerchandiseSelection.tsx +45 -6
  70. package/src/form/payment/FeeBox.tsx +2 -3
  71. package/src/locales/cs.tsx +3 -0
  72. package/src/locales/en.tsx +3 -0
  73. package/src/locales/es.tsx +3 -0
  74. package/src/locales/pl.tsx +3 -0
  75. package/src/locales/sk.tsx +3 -0
  76. package/src/locales/uk.tsx +3 -0
  77. package/src/modules/shopping-cart.ts +14 -11
  78. package/src/utils/data/shopping-cart.ts +5 -0
  79. package/src/utils/types/shopping-cart.type.ts +40 -36
  80. package/src/utils/types/ticket.type.ts +1 -2
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import { Iconify, Image, TextIconLabel } from '@components';
3
3
  import { Box, Divider, Grid, IconButton, Stack, Typography } from '@mui/material';
4
4
  import PaymentOverviewCheckbox from '@form/payment/PaymentOverviewCheckbox.tsx';
@@ -16,16 +16,14 @@ import FeeBox from '@form/payment/FeeBox';
16
16
  import { IPromoCode } from '@utils/types/promo-code.type';
17
17
  import { fCurrency } from '@utils/formatNumber';
18
18
  import { IEventProductForm, ISelectedProductVariant } from '@utils/types/product.type';
19
- import { isEqual } from 'lodash';
20
19
  import calendarIcon from '@iconify/icons-carbon/calendar';
21
20
  import userIcon from '@iconify/icons-carbon/user';
22
21
  import { EventType } from '@utils/data/event';
23
22
  import dayjs from 'dayjs';
24
23
  import useGlobal from '@hooks/useGlobal.ts';
25
- import { getCreateShoppingCart, recalculateCart } from '@modules/shopping-cart.ts';
26
- import { ICalculateShoppingCartDto, IShoppingCart } from '@utils/types/shopping-cart.type.ts';
24
+ import { calculateCart, getCart } from '@modules/shopping-cart.ts';
25
+ import { ICalculateShoppingCartDto, IShoppingCartDto } from '@utils/types/shopping-cart.type.ts';
27
26
  import { PromoCodeTypes } from '@utils/data/promo-code.ts';
28
- import { useFirstRender } from '@hooks/useFirstRender.ts';
29
27
  import useActiveEventProducts from '@hooks/data/useActiveEventProducts';
30
28
  import useDebounce from '@hooks/useDebounce';
31
29
 
@@ -35,9 +33,6 @@ interface Props {
35
33
 
36
34
  const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
37
35
  const { t, lang, options } = useGlobal();
38
- const firstRender = useFirstRender();
39
- const secondRender = useRef(false);
40
- const lastCartRef = useRef<IShoppingCart | null>(null);
41
36
  const xs = useResponsive('only', 'xs');
42
37
  const md = useResponsive('only', 'md');
43
38
  const isMobile = useResponsive('down', 'md');
@@ -139,44 +134,45 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
139
134
 
140
135
  const shoppingCartBody = useMemo<ICalculateShoppingCartDto>(
141
136
  () => ({
142
- uuid: uuid,
137
+ uuid,
143
138
  eventId: event.id,
144
- tickets: Object.entries(values.tickets).reduce(
145
- (acc, [eventId, items]) => {
146
- acc[Number(eventId)] = items
147
- .filter((item) => !!item.quantity && !!item.releaseId)
139
+ tickets: Object.fromEntries(
140
+ Object.entries(values.tickets).map(([id, items]) => [
141
+ id,
142
+ items
143
+ .filter((item) => item.quantity && item.releaseId)
148
144
  .map((item) => ({
149
- releaseId: Number(item.releaseId),
150
- quantity: Number(item.quantity),
151
- price: Number(item.price),
152
- itemName: item.itemName,
153
- products: item.products,
154
- extraFields: item.extraFields,
155
- eventTimeslotId: Number(eventTimeslotId) || null,
156
- }));
157
- return acc;
158
- },
159
- {} as Record<number, ITicketFormTicket[]>
145
+ ...item,
146
+ eventTimeslotId: eventTimeslotId ?? null,
147
+ })),
148
+ ])
160
149
  ),
161
- products: Object.entries(values.products).reduce(
162
- (acc, [eventId, items]) => {
163
- acc[Number(eventId)] = items.map((item) => ({
164
- eventProductVariantId: item.eventProductVariantId,
165
- productVariantId: item.productVariantId,
166
- quantity: item.quantity,
167
- price: item.price,
168
- excludedShippingMethodIds: item.excludedShippingMethodIds,
169
- }));
170
- return acc;
171
- },
172
- {} as Record<number, IEventProductForm[]>
150
+ products: Object.fromEntries(
151
+ Object.entries(values.products).map(([id, items]) => [
152
+ id,
153
+ items.map(
154
+ ({
155
+ eventProductVariantId,
156
+ productVariantId,
157
+ quantity,
158
+ price,
159
+ excludedShippingMethodIds,
160
+ }) => ({
161
+ eventProductVariantId,
162
+ productVariantId,
163
+ quantity,
164
+ price,
165
+ excludedShippingMethodIds,
166
+ })
167
+ ),
168
+ ])
173
169
  ),
174
170
  shipping: {
175
- shippingMethodId: shipping?.shippingMethodId ? Number(shipping.shippingMethodId) : null,
176
- branchId: shipping?.branchId,
177
- price: shipping?.price ? Number(shipping.price) : 0,
171
+ shippingMethodId: shipping?.shippingMethodId ?? null,
172
+ branchId: shipping?.branchId ?? null,
173
+ price: shipping?.price ?? 0,
178
174
  },
179
- promoCodeIds: promoCodes?.map((p) => p.id) || null,
175
+ promoCodeIds: promoCodes?.map((p) => p.id) ?? null,
180
176
  language: lang,
181
177
  firstName,
182
178
  lastName,
@@ -184,8 +180,8 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
184
180
  phone,
185
181
  birthdate,
186
182
  gender,
187
- paymentMethodId: paymentMethodId === null ? null : Number(paymentMethodId),
188
- paymentMethodOptionId: paymentMethodOptionId === null ? null : Number(paymentMethodOptionId),
183
+ paymentMethodId: paymentMethodId ? Number(paymentMethodId) : null,
184
+ paymentMethodOptionId: paymentMethodOptionId ? Number(paymentMethodOptionId) : null,
189
185
  ticketInsurance,
190
186
  smsNotification,
191
187
  }),
@@ -194,9 +190,7 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
194
190
  event.id,
195
191
  tickets,
196
192
  products,
197
- shipping.shippingMethodId,
198
- shipping.branchId,
199
- shipping.price,
193
+ shipping,
200
194
  promoCodes,
201
195
  lang,
202
196
  firstName,
@@ -215,13 +209,13 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
215
209
 
216
210
  const debouncedShoppingCartBody = useDebounce(shoppingCartBody, 300);
217
211
 
218
- const setShoppingCartValues = (data: IShoppingCart, recalculate = false) => {
212
+ const setShoppingCartValues = (data: IShoppingCartDto, recalculate = false) => {
219
213
  if (!recalculate) {
220
214
  const hasTickets =
221
215
  !!data.tickets && typeof data.tickets === 'object' && Object.keys(data.tickets).length > 0;
222
216
 
223
217
  if (hasTickets) {
224
- setValue('tickets', { ...tickets, ...data.tickets });
218
+ setValue('tickets', data.tickets);
225
219
  }
226
220
 
227
221
  const hasProducts =
@@ -230,7 +224,7 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
230
224
  Object.keys(data.products).length > 0;
231
225
 
232
226
  if (hasProducts) {
233
- setValue('products', { ...products, ...data.products });
227
+ setValue('products', data.products);
234
228
  }
235
229
 
236
230
  setValue('promoCodes', data.promoCodes || []);
@@ -259,11 +253,11 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
259
253
  setValue('totalFee', feeTotal);
260
254
 
261
255
  setShippingFee(data.shippingFee);
262
- setValue('ticketInsurance', data.ticketInsurance || false);
263
- setValue('ticketInsurancePrice', data.ticketInsurancePrice || 0);
264
- setValue('ticketInsurancePricePerUnit', data.ticketInsurancePricePerUnit || 0);
265
- setValue('smsNotification', data.smsNotification || false);
266
- setValue('smsNotificationPrice', data.smsNotificationPrice || 0);
256
+ setValue('ticketInsurance', data.ticketInsurance.enabled);
257
+ setValue('ticketInsurancePrice', data.ticketInsurance.price);
258
+ setValue('ticketInsurancePricePerUnit', data.ticketInsurance.pricePerUnit);
259
+ setValue('smsNotification', data.smsNotification.enabled);
260
+ setValue('smsNotificationPrice', data.smsNotification.price);
267
261
  };
268
262
 
269
263
  const totalItemCount = useMemo(() => {
@@ -335,30 +329,16 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
335
329
  return discount;
336
330
  }, [promoCodes, flatTickets, flatProducts]);
337
331
 
338
- useEffect(() => {
339
- if (!secondRender.current) {
340
- secondRender.current = true;
341
- } else {
342
- return;
343
- }
344
-
345
- (async () => {
346
- const existing = localStorage.getItem('cartToken');
347
- const res = await getCreateShoppingCart(existing, event.id);
348
- const data: IShoppingCart = res.data;
349
-
350
- localStorage.setItem('cartToken', data.uuid);
351
- setShoppingCartValues(data);
352
- })();
332
+ const hasOrderId = useMemo(() => {
333
+ const urlParams = new URLSearchParams(window.location.search);
334
+ return urlParams.has('id');
353
335
  }, []);
354
336
 
355
337
  useEffect(() => {
356
- if (firstRender) {
357
- return;
338
+ if (!hasOrderId) {
339
+ recalculateItems(debouncedShoppingCartBody);
358
340
  }
359
-
360
- recalculateItems(debouncedShoppingCartBody);
361
- }, [debouncedShoppingCartBody]);
341
+ }, [debouncedShoppingCartBody, hasOrderId]);
362
342
 
363
343
  /** Iframe code **/
364
344
  useEffect(() => {
@@ -378,27 +358,30 @@ const PaymentOverviewBox: React.FC<Props> = ({ event }) => {
378
358
  };
379
359
  /** End Iframe code **/
380
360
 
381
- const updateCartIfChanged = (newCart: IShoppingCart, recalculate = false) => {
382
- if (isEqual(lastCartRef.current, newCart)) {
383
- return;
384
- }
385
-
386
- lastCartRef.current = newCart;
387
- setShoppingCartValues(newCart, recalculate);
388
- };
389
-
390
361
  const recalculateItems = useCallback(async (body: ICalculateShoppingCartDto) => {
391
- const { uuid } = body;
392
- if (!uuid) return;
393
362
  try {
394
- const response = await recalculateCart(body);
395
- const cart = response?.data?.data;
363
+ const isInitial = !body.uuid;
364
+
365
+ let response: any;
366
+ if (isInitial) {
367
+ const existing = localStorage.getItem('cartToken');
368
+ const storedUuid =
369
+ existing && existing !== 'undefined' && existing !== 'null' ? existing : null;
370
+ response = await getCart(body.eventId, storedUuid);
371
+ } else {
372
+ response = await calculateCart(body);
373
+ }
374
+
375
+ const cart = response?.data;
396
376
 
397
377
  if (cart) {
398
- updateCartIfChanged(cart, true);
378
+ if (cart.uuid) {
379
+ localStorage.setItem('cartToken', cart.uuid);
380
+ }
381
+ setShoppingCartValues(cart, !isInitial);
399
382
  }
400
383
  } catch (err) {
401
- console.error('Error recalculating cart:', err);
384
+ console.error('Error calculating cart:', err);
402
385
  }
403
386
  }, []);
404
387
 
@@ -220,7 +220,6 @@ const TicketForm: React.FC<Props> = ({
220
220
  smsNotification: false,
221
221
  smsNotificationPrice: 0,
222
222
  iframeCampaignId: undefined,
223
- promoCode: null,
224
223
  promoCodes: [],
225
224
  products: {},
226
225
  shipping: {
@@ -327,6 +326,7 @@ const TicketForm: React.FC<Props> = ({
327
326
  } else {
328
327
  try {
329
328
  const data: ITicketBody = cloneObject(values);
329
+ data.paymentMethodId = Number(values.paymentMethodId);
330
330
  data.promoCodeIds = values.promoCodes?.map((item) => item.id);
331
331
  const urlParams = new URLSearchParams(window.location.search);
332
332
  const iframeCallback = urlParams.get('callback');
@@ -343,14 +343,14 @@ const TicketForm: React.FC<Props> = ({
343
343
  products: item.products,
344
344
  extraFields: item.extraFields,
345
345
  eventTimeslotId: item.eventTimeslotId,
346
- locationId: item.location?.id,
347
- locationDescription: item.location?.locationDescription,
346
+ location: item.location,
348
347
  }));
349
348
  return acc;
350
349
  },
351
350
  {} as Record<number, ITicketFormTicket[]>
352
351
  );
353
352
  const { data: orderData } = await postOrder(data);
353
+ localStorage.removeItem('cartToken');
354
354
  const items = [
355
355
  ...orderData.tickets.map((ticket) => ({
356
356
  item_id: ticket.number,
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { useFormContext, useWatch } from 'react-hook-form';
3
3
  import {
4
4
  Box,
@@ -39,6 +39,7 @@ const TicketSelection: React.FC<Props> = ({ event }) => {
39
39
  }) as ITicketFormTicket[];
40
40
  const eventTimeslotId = watch('eventTimeslotId');
41
41
  const [soldOutReleaseCategoryNames, setSoldOutReleaseCategoryNames] = useState<string[]>([]);
42
+ const isProcessingRef = useRef(false);
42
43
  const { data: activeReleases, mutate } = useEventActiveReleases(
43
44
  event.id,
44
45
  false,
@@ -48,8 +49,8 @@ const TicketSelection: React.FC<Props> = ({ event }) => {
48
49
  const showLoading = !activeReleases && event.type !== EventType.RECURRING;
49
50
 
50
51
  useEffect(() => {
51
- selectedTickets();
52
- }, [tickets]);
52
+ if (!isProcessingRef.current) selectedTickets();
53
+ }, [tickets, activeReleases]);
53
54
 
54
55
  const isReleaseSelected = (id: number) => !!tickets.find((ticket) => ticket.releaseId === id);
55
56
 
@@ -139,7 +140,7 @@ const TicketSelection: React.FC<Props> = ({ event }) => {
139
140
  soldOutReleaseCategories?.map((item) => item.releaseCategoryName) || []
140
141
  );
141
142
 
142
- if (
143
+ const shouldAddRow =
143
144
  (soldOutReleaseCategories &&
144
145
  selectedReleaseIsSoldOut(releases) &&
145
146
  tickets.length < soldOutReleaseCategories.length + countUnlockedReleases() &&
@@ -149,8 +150,15 @@ const TicketSelection: React.FC<Props> = ({ event }) => {
149
150
  activeReleases?.length > tickets.length &&
150
151
  tickets.length < soldOutReleaseCategories.length + countUnlockedReleases() &&
151
152
  !allFilled.length) ||
152
- (tickets.length < countReleaseCategories() && !allFilled.length)
153
- ) {
153
+ (tickets.length < countReleaseCategories() && !allFilled.length);
154
+
155
+ const shouldRemoveEmptyRows =
156
+ allFilled.length > 0 &&
157
+ tickets.length > 1 &&
158
+ (!soldOutReleaseCategories?.length || !selectedReleaseIsSoldOut(releases));
159
+
160
+ if (shouldAddRow) {
161
+ isProcessingRef.current = true;
154
162
  setValue(`tickets.${event.id}`, [
155
163
  ...tickets,
156
164
  {
@@ -162,6 +170,14 @@ const TicketSelection: React.FC<Props> = ({ event }) => {
162
170
  extraFields: [],
163
171
  },
164
172
  ]);
173
+ setTimeout(() => (isProcessingRef.current = false), 0);
174
+ } else if (shouldRemoveEmptyRows) {
175
+ const filledTickets = tickets.filter((item) => item.releaseId && item.quantity);
176
+ if (filledTickets.length < tickets.length) {
177
+ isProcessingRef.current = true;
178
+ setValue(`tickets.${event.id}`, filledTickets);
179
+ setTimeout(() => (isProcessingRef.current = false), 0);
180
+ }
165
181
  }
166
182
  };
167
183
 
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useRef } from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import { useFormContext, useWatch } from 'react-hook-form';
3
3
  import { Box, Divider, Skeleton, Stack, Typography } from '@mui/material';
4
4
  import { ITicketForm, ITicketFormTicket } from '@utils/types/ticket.type';
@@ -22,6 +22,7 @@ const TicketWithMerchandiseSelection: React.FC<Props> = ({ event }) => {
22
22
  const { setValue, watch } = useFormContext<ITicketForm>();
23
23
  const tickets: ITicketFormTicket[] = useWatch({ name: `tickets.${event.id}`, defaultValue: [] });
24
24
  const eventTimeslotId = watch('eventTimeslotId');
25
+ const isProcessingRef = useRef(false);
25
26
  const { data: activeReleases, mutate } = useEventActiveReleases(
26
27
  event.id,
27
28
  false,
@@ -29,8 +30,8 @@ const TicketWithMerchandiseSelection: React.FC<Props> = ({ event }) => {
29
30
  );
30
31
 
31
32
  useEffect(() => {
32
- selectedTickets();
33
- }, [tickets]);
33
+ if (!isProcessingRef.current) selectedTickets();
34
+ }, [tickets, activeReleases]);
34
35
 
35
36
  const countReleaseCategories = (): number => {
36
37
  const grouped = groupBy(activeReleases || [], 'releaseCategoryName');
@@ -64,7 +65,30 @@ const TicketWithMerchandiseSelection: React.FC<Props> = ({ event }) => {
64
65
  )
65
66
  );
66
67
 
67
- if (
68
+ // Unlock next releases when current release is sold out
69
+ if (soldOutReleaseCategories?.length && activeReleases) {
70
+ let hasChanges = false;
71
+ const updatedReleases = activeReleases.map((release) => {
72
+ // For each sold-out release, find and unlock the next one
73
+ const soldOutRelease = soldOutReleaseCategories.find(
74
+ (soldOut) =>
75
+ soldOut.releaseCategoryName === release.releaseCategoryName &&
76
+ soldOut.order === release.order - 1 &&
77
+ release.locked
78
+ );
79
+ if (soldOutRelease) {
80
+ hasChanges = true;
81
+ return { ...release, locked: false };
82
+ }
83
+ return release;
84
+ });
85
+
86
+ if (hasChanges) {
87
+ await mutate(updatedReleases, false);
88
+ }
89
+ }
90
+
91
+ const shouldAddRow =
68
92
  (soldOutReleaseCategories &&
69
93
  selectedReleaseIsSoldOut(releases) &&
70
94
  tickets.length < soldOutReleaseCategories.length + countUnlockedReleases() &&
@@ -74,8 +98,15 @@ const TicketWithMerchandiseSelection: React.FC<Props> = ({ event }) => {
74
98
  activeReleases?.length > tickets.length &&
75
99
  tickets.length < soldOutReleaseCategories.length + countUnlockedReleases() &&
76
100
  !allFilled.length) ||
77
- (tickets.length < countReleaseCategories() && !allFilled.length)
78
- ) {
101
+ (tickets.length < countReleaseCategories() && !allFilled.length);
102
+
103
+ const shouldRemoveEmptyRows =
104
+ allFilled.length > 0 &&
105
+ tickets.length > 1 &&
106
+ (!soldOutReleaseCategories?.length || !selectedReleaseIsSoldOut(releases));
107
+
108
+ if (shouldAddRow) {
109
+ isProcessingRef.current = true;
79
110
  setValue(`tickets.${event.id}`, [
80
111
  ...tickets,
81
112
  {
@@ -87,6 +118,14 @@ const TicketWithMerchandiseSelection: React.FC<Props> = ({ event }) => {
87
118
  extraFields: [],
88
119
  },
89
120
  ]);
121
+ setTimeout(() => (isProcessingRef.current = false), 0);
122
+ } else if (shouldRemoveEmptyRows) {
123
+ const filledTickets = tickets.filter((item) => item.releaseId && item.quantity);
124
+ if (filledTickets.length < tickets.length) {
125
+ isProcessingRef.current = true;
126
+ setValue(`tickets.${event.id}`, filledTickets);
127
+ setTimeout(() => (isProcessingRef.current = false), 0);
128
+ }
90
129
  }
91
130
  };
92
131
 
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Box, Stack, Tooltip, Typography } from '@mui/material';
3
- import { OrderFeeType } from '@utils/data/order';
4
- import { fCurrency, fPercent } from '@utils/formatNumber';
3
+ import { fCurrency } from '@utils/formatNumber';
5
4
  import { Iconify } from '@components/iconify';
6
5
  import { IEvent } from '@utils/types/event.type';
7
6
  import useResponsive from '@hooks/useResponsive';
@@ -23,7 +22,7 @@ const FeeBox: React.FC<Props> = ({ event, align = 'left' }) => {
23
22
 
24
23
  return (
25
24
  <Box mb={1} textAlign={isRight ? 'right' : undefined}>
26
- {values.promoCode && (
25
+ {values.promoCodes.length > 0 && (
27
26
  <Stack
28
27
  direction="row"
29
28
  justifyContent={isRight ? 'flex-end' : 'space-between'}
@@ -61,6 +61,9 @@ const cs = {
61
61
  invalid_date: 'Datum není platné.',
62
62
  terms_and_conditions: 'Musíte souhlasit s obchodními podmínkami.',
63
63
  promo_code_invalid: 'Slevový kód není platný.',
64
+ promo_code_applied: 'Tento slevový kód již byl použit.',
65
+ promo_code_cant_combine: 'Tento slevový kód nelze kombinovat s jinými kódy.',
66
+ min_purchase_not_met: 'Minimální částka nákupu nebyla dosažena: ',
64
67
  count_tickets_or_products: 'Musíte vybrat alespoň 1 vstupenku nebo produkt.',
65
68
  },
66
69
  },
@@ -61,6 +61,9 @@ const en = {
61
61
  invalid_date: 'Date is not valid.',
62
62
  terms_and_conditions: 'You must agree to the terms and conditions.',
63
63
  promo_code_invalid: 'Promo code is not valid.',
64
+ promo_code_applied: 'This promo code has already been applied.',
65
+ promo_code_cant_combine: 'This promo code cannot be combined with other codes.',
66
+ min_purchase_not_met: 'Minimum purchase amount not met: ',
64
67
  count_tickets_or_products: 'You must select at least 1 ticket or product.',
65
68
  },
66
69
  },
@@ -61,6 +61,9 @@ const es = {
61
61
  invalid_date: 'La fecha no es válida.',
62
62
  terms_and_conditions: 'Debes aceptar los términos y condiciones.',
63
63
  promo_code_invalid: 'El código promocional no es válido.',
64
+ promo_code_applied: 'Este código promocional ya ha sido aplicado.',
65
+ promo_code_cant_combine: 'Este código promocional no se puede combinar con otros códigos.',
66
+ min_purchase_not_met: 'No se alcanzó el monto mínimo de compra: ',
64
67
  count_tickets_or_products: 'Debes seleccionar al menos 1 entrada o producto.',
65
68
  },
66
69
  },
@@ -61,6 +61,9 @@ const pl = {
61
61
  invalid_date: 'Data jest nieprawidłowa.',
62
62
  terms_and_conditions: 'Musisz zaakceptować regulamin.',
63
63
  promo_code_invalid: 'Kod promocyjny jest nieprawidłowy.',
64
+ promo_code_applied: 'Ten kod promocyjny został już zastosowany.',
65
+ promo_code_cant_combine: 'Ten kod promocyjny nie może być łączony z innymi kodami.',
66
+ min_purchase_not_met: 'Minimalna kwota zakupu nie została osiągnięta: ',
64
67
  count_tickets_or_products: 'Musisz wybrać co najmniej 1 bilet lub produkt.',
65
68
  },
66
69
  },
@@ -61,6 +61,9 @@ const sk = {
61
61
  invalid_date: 'Dátum nie je platný.',
62
62
  terms_and_conditions: 'Musíte súhlasiť s podmienkami.',
63
63
  promo_code_invalid: 'Promo kód nie je platný.',
64
+ promo_code_applied: 'Tento promo kód už bol použitý.',
65
+ promo_code_cant_combine: 'Tento promo kód nemožno kombinovať s inými kódmi.',
66
+ min_purchase_not_met: 'Minimálna suma nákupu nebola dosiahnutá: ',
64
67
  count_tickets_or_products: 'Musíte vybrať aspoň 1 vstupenku alebo produkt.',
65
68
  },
66
69
  },
@@ -61,6 +61,9 @@ const uk = {
61
61
  invalid_date: 'Дата недійсна.',
62
62
  terms_and_conditions: 'Ви повинні погодитися з умовами.',
63
63
  promo_code_invalid: 'Промокод недійсний.',
64
+ promo_code_applied: 'Цей промокод вже було застосовано.',
65
+ promo_code_cant_combine: 'Цей промокод не можна комбінувати з іншими кодами.',
66
+ min_purchase_not_met: 'Мінімальну суму покупки не досягнуто: ',
64
67
  count_tickets_or_products: 'Виберіть хоча б 1 квиток або продукт.',
65
68
  },
66
69
  },
@@ -1,16 +1,19 @@
1
1
  import api from '@utils/axios';
2
2
  import { IResponse } from '@utils/types/global.type.ts';
3
- import { ICalculateShoppingCartDto, IShoppingCart } from '@utils/types/shopping-cart.type.ts';
3
+ import { ICalculateShoppingCartDto, IShoppingCartDto } from '@utils/types/shopping-cart.type.ts';
4
4
 
5
- export const getCreateShoppingCart = (uuid: string | null, eventId: number) =>
6
- api
7
- .post('/v1/shopping-cart', {
8
- eventId,
9
- uuid,
10
- })
11
- .then((res) => res.data.data);
5
+ export const getCart = (
6
+ eventId: number,
7
+ uuid?: string | null
8
+ ): Promise<IResponse<IShoppingCartDto>> => {
9
+ const params = new URLSearchParams({ eventId: eventId.toString() });
10
+ if (uuid) {
11
+ params.append('uuid', uuid);
12
+ }
13
+ return api.get(`/v1/shopping-cart?${params.toString()}`).then((res) => res.data.data);
14
+ };
12
15
 
13
- export const recalculateCart = (
16
+ export const calculateCart = (
14
17
  data: ICalculateShoppingCartDto
15
- ): Promise<IResponse<{ data: IShoppingCart }>> =>
16
- api.post('/v1/shopping-cart/recalculate', data).then((res) => res.data);
18
+ ): Promise<IResponse<IShoppingCartDto>> =>
19
+ api.post('/v1/shopping-cart', data).then((res) => res.data.data);
@@ -0,0 +1,5 @@
1
+ export enum ShoppingCartStatus {
2
+ NEW = 'NEW',
3
+ ABANDONED = 'ABANDONED',
4
+ COMPLETED = 'COMPLETED',
5
+ }