@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.
- package/.claude/settings.local.json +9 -0
- package/dist/cjs/form/Payment.js +0 -2
- package/dist/cjs/form/Payment.js.map +1 -1
- package/dist/cjs/form/PaymentOverviewBox.js +55 -77
- package/dist/cjs/form/PaymentOverviewBox.js.map +1 -1
- package/dist/cjs/form/TicketForm.js +3 -3
- package/dist/cjs/form/TicketForm.js.map +1 -1
- package/dist/cjs/form/TicketSelection.js +20 -4
- package/dist/cjs/form/TicketSelection.js.map +1 -1
- package/dist/cjs/form/TicketWithMerchandiseSelection.js +38 -4
- package/dist/cjs/form/TicketWithMerchandiseSelection.js.map +1 -1
- package/dist/cjs/form/payment/FeeBox.js +1 -1
- package/dist/cjs/form/payment/FeeBox.js.map +1 -1
- package/dist/cjs/locales/cs.js +3 -0
- package/dist/cjs/locales/cs.js.map +1 -1
- package/dist/cjs/locales/en.js +3 -0
- package/dist/cjs/locales/en.js.map +1 -1
- package/dist/cjs/locales/es.js +3 -0
- package/dist/cjs/locales/es.js.map +1 -1
- package/dist/cjs/locales/pl.js +3 -0
- package/dist/cjs/locales/pl.js.map +1 -1
- package/dist/cjs/locales/sk.js +3 -0
- package/dist/cjs/locales/sk.js.map +1 -1
- package/dist/cjs/locales/uk.js +3 -0
- package/dist/cjs/locales/uk.js.map +1 -1
- package/dist/cjs/modules/shopping-cart.js +10 -9
- package/dist/cjs/modules/shopping-cart.js.map +1 -1
- package/dist/esm/form/Payment.js +0 -2
- package/dist/esm/form/Payment.js.map +1 -1
- package/dist/esm/form/PaymentOverviewBox.js +57 -79
- package/dist/esm/form/PaymentOverviewBox.js.map +1 -1
- package/dist/esm/form/TicketForm.js +3 -3
- package/dist/esm/form/TicketForm.js.map +1 -1
- package/dist/esm/form/TicketSelection.js +21 -5
- package/dist/esm/form/TicketSelection.js.map +1 -1
- package/dist/esm/form/TicketWithMerchandiseSelection.js +39 -5
- package/dist/esm/form/TicketWithMerchandiseSelection.js.map +1 -1
- package/dist/esm/form/payment/FeeBox.js +1 -1
- package/dist/esm/form/payment/FeeBox.js.map +1 -1
- package/dist/esm/locales/cs.js +3 -0
- package/dist/esm/locales/cs.js.map +1 -1
- package/dist/esm/locales/en.js +3 -0
- package/dist/esm/locales/en.js.map +1 -1
- package/dist/esm/locales/es.js +3 -0
- package/dist/esm/locales/es.js.map +1 -1
- package/dist/esm/locales/pl.js +3 -0
- package/dist/esm/locales/pl.js.map +1 -1
- package/dist/esm/locales/sk.js +3 -0
- package/dist/esm/locales/sk.js.map +1 -1
- package/dist/esm/locales/uk.js +3 -0
- package/dist/esm/locales/uk.js.map +1 -1
- package/dist/esm/modules/shopping-cart.js +9 -8
- package/dist/esm/modules/shopping-cart.js.map +1 -1
- package/dist/types/locales/cs.d.ts +3 -0
- package/dist/types/locales/en.d.ts +3 -0
- package/dist/types/locales/es.d.ts +3 -0
- package/dist/types/locales/pl.d.ts +3 -0
- package/dist/types/locales/sk.d.ts +3 -0
- package/dist/types/locales/uk.d.ts +3 -0
- package/dist/types/modules/shopping-cart.d.ts +3 -5
- package/dist/types/utils/data/shopping-cart.d.ts +5 -0
- package/dist/types/utils/types/shopping-cart.type.d.ts +42 -40
- package/dist/types/utils/types/ticket.type.d.ts +1 -2
- package/package.json +1 -1
- package/src/form/Payment.tsx +0 -2
- package/src/form/PaymentOverviewBox.tsx +71 -88
- package/src/form/TicketForm.tsx +3 -3
- package/src/form/TicketSelection.tsx +22 -6
- package/src/form/TicketWithMerchandiseSelection.tsx +45 -6
- package/src/form/payment/FeeBox.tsx +2 -3
- package/src/locales/cs.tsx +3 -0
- package/src/locales/en.tsx +3 -0
- package/src/locales/es.tsx +3 -0
- package/src/locales/pl.tsx +3 -0
- package/src/locales/sk.tsx +3 -0
- package/src/locales/uk.tsx +3 -0
- package/src/modules/shopping-cart.ts +14 -11
- package/src/utils/data/shopping-cart.ts +5 -0
- package/src/utils/types/shopping-cart.type.ts +40 -36
- package/src/utils/types/ticket.type.ts +1 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo,
|
|
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 {
|
|
26
|
-
import { ICalculateShoppingCartDto,
|
|
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
|
|
137
|
+
uuid,
|
|
143
138
|
eventId: event.id,
|
|
144
|
-
tickets: Object.
|
|
145
|
-
(
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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.
|
|
162
|
-
(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
|
176
|
-
branchId: shipping?.branchId,
|
|
177
|
-
price: shipping?.price
|
|
171
|
+
shippingMethodId: shipping?.shippingMethodId ?? null,
|
|
172
|
+
branchId: shipping?.branchId ?? null,
|
|
173
|
+
price: shipping?.price ?? 0,
|
|
178
174
|
},
|
|
179
|
-
promoCodeIds: promoCodes?.map((p) => p.id)
|
|
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
|
|
188
|
-
paymentMethodOptionId: 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
|
|
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:
|
|
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',
|
|
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',
|
|
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
|
|
263
|
-
setValue('ticketInsurancePrice', data.
|
|
264
|
-
setValue('ticketInsurancePricePerUnit', data.
|
|
265
|
-
setValue('smsNotification', data.smsNotification
|
|
266
|
-
setValue('smsNotificationPrice', data.
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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 (
|
|
357
|
-
|
|
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
|
|
395
|
-
|
|
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
|
-
|
|
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
|
|
384
|
+
console.error('Error calculating cart:', err);
|
|
402
385
|
}
|
|
403
386
|
}, []);
|
|
404
387
|
|
package/src/form/TicketForm.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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 {
|
|
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.
|
|
25
|
+
{values.promoCodes.length > 0 && (
|
|
27
26
|
<Stack
|
|
28
27
|
direction="row"
|
|
29
28
|
justifyContent={isRight ? 'flex-end' : 'space-between'}
|
package/src/locales/cs.tsx
CHANGED
|
@@ -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
|
},
|
package/src/locales/en.tsx
CHANGED
|
@@ -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
|
},
|
package/src/locales/es.tsx
CHANGED
|
@@ -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
|
},
|
package/src/locales/pl.tsx
CHANGED
|
@@ -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
|
},
|
package/src/locales/sk.tsx
CHANGED
|
@@ -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
|
},
|
package/src/locales/uk.tsx
CHANGED
|
@@ -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,
|
|
3
|
+
import { ICalculateShoppingCartDto, IShoppingCartDto } from '@utils/types/shopping-cart.type.ts';
|
|
4
4
|
|
|
5
|
-
export const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
.
|
|
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
|
|
16
|
+
export const calculateCart = (
|
|
14
17
|
data: ICalculateShoppingCartDto
|
|
15
|
-
): Promise<IResponse<
|
|
16
|
-
api.post('/v1/shopping-cart
|
|
18
|
+
): Promise<IResponse<IShoppingCartDto>> =>
|
|
19
|
+
api.post('/v1/shopping-cart', data).then((res) => res.data.data);
|