@eventlook/sdk 1.5.0-beta.6 → 1.5.0-beta.8

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 (91) hide show
  1. package/.claude/settings.local.json +6 -10
  2. package/.env.example +1 -0
  3. package/README.md +18 -16
  4. package/dist/cjs/{index-DvUR1fp8.js → index-CUIxdwQn.js} +3340 -577
  5. package/dist/cjs/index-CUIxdwQn.js.map +1 -0
  6. package/dist/cjs/index-D5rQiSGP.js +38574 -0
  7. package/dist/cjs/index-D5rQiSGP.js.map +1 -0
  8. package/dist/cjs/index.js +2 -2
  9. package/dist/cjs/{index.umd-6SU6nkkJ.js → index.umd-BoFEW91M.js} +9 -19
  10. package/dist/cjs/index.umd-BoFEW91M.js.map +1 -0
  11. package/dist/cjs/index.umd-BzSM62qM.js +13397 -0
  12. package/dist/cjs/index.umd-BzSM62qM.js.map +1 -0
  13. package/dist/esm/index-Cm7V8Zl3.js +38571 -0
  14. package/dist/esm/index-Cm7V8Zl3.js.map +1 -0
  15. package/dist/esm/{index-BlTqx0jm.js → index-fvLIN6eP.js} +3327 -563
  16. package/dist/esm/index-fvLIN6eP.js.map +1 -0
  17. package/dist/esm/index.js +2 -2
  18. package/dist/esm/{index.umd-Dn0hjh7E.js → index.umd-BKBHcCnm.js} +9 -19
  19. package/dist/esm/index.umd-BKBHcCnm.js.map +1 -0
  20. package/dist/esm/index.umd-bIV_YpEF.js +13395 -0
  21. package/dist/esm/index.umd-bIV_YpEF.js.map +1 -0
  22. package/dist/types/components/hook-form/FormProvider.d.ts +2 -1
  23. package/dist/types/form/PaymentOverviewBox.d.ts +2 -0
  24. package/dist/types/form/PaymentOverviewDrawer.d.ts +10 -0
  25. package/dist/types/form/TicketForm.d.ts +1 -0
  26. package/dist/types/form/index.d.ts +2 -1
  27. package/dist/types/form/merchandise/MerchandiseSelection.d.ts +9 -0
  28. package/dist/types/form/merchandise/MerchandiseSlider.d.ts +10 -0
  29. package/dist/types/form/payment/PaymentOverviewCheckbox.d.ts +0 -4
  30. package/dist/types/form/product/ProductVariantsDialog.d.ts +3 -1
  31. package/dist/types/form/services/index.d.ts +7 -0
  32. package/dist/types/form/style.d.ts +1 -0
  33. package/dist/types/form/tickets/ReleaseDescription.d.ts +10 -0
  34. package/dist/types/form/tickets/ReleaseWithMerchandise.d.ts +12 -0
  35. package/dist/types/form/tickets/TicketQuantityControl.d.ts +13 -0
  36. package/dist/types/form/tickets/TicketSelectionMobile.d.ts +16 -0
  37. package/dist/types/hooks/useScrollToFirstError.d.ts +4 -0
  38. package/dist/types/locales/cs.d.ts +22 -0
  39. package/dist/types/locales/en.d.ts +22 -0
  40. package/dist/types/locales/es.d.ts +22 -0
  41. package/dist/types/locales/pl.d.ts +22 -0
  42. package/dist/types/locales/sk.d.ts +22 -0
  43. package/dist/types/locales/uk.d.ts +22 -0
  44. package/dist/types/utils/data/global.d.ts +1 -0
  45. package/dist/types/utils/data/ticket.d.ts +1 -0
  46. package/package.json +10 -4
  47. package/rollup.config.mjs +7 -12
  48. package/src/components/hook-form/FormProvider.tsx +5 -2
  49. package/src/form/ChildEventDialog.tsx +3 -3
  50. package/src/form/ContactPerson.tsx +1 -1
  51. package/src/form/PaymentOverviewBox.tsx +96 -123
  52. package/src/form/PaymentOverviewDrawer.tsx +445 -0
  53. package/src/form/PaymentPending.tsx +19 -4
  54. package/src/form/ReleaseWithMerchandise.tsx +4 -4
  55. package/src/form/Shipping.tsx +48 -33
  56. package/src/form/TicketForm.tsx +146 -41
  57. package/src/form/index.tsx +3 -1
  58. package/src/form/merchandise/MerchandiseSelection.tsx +24 -0
  59. package/src/form/merchandise/MerchandiseSlider.tsx +62 -0
  60. package/src/form/payment/FeeBox.tsx +4 -31
  61. package/src/form/payment/PaymentOverviewCheckbox.tsx +68 -69
  62. package/src/form/product/ProductCard.tsx +258 -59
  63. package/src/form/product/ProductVariantsDialog.tsx +292 -139
  64. package/src/form/services/index.tsx +262 -0
  65. package/src/form/style.ts +16 -4
  66. package/src/form/tickets/ReleaseDescription.tsx +46 -0
  67. package/src/form/tickets/ReleaseWithMerchandise.tsx +267 -0
  68. package/src/form/tickets/TicketQuantityControl.tsx +100 -0
  69. package/src/form/tickets/TicketSelection.tsx +236 -0
  70. package/src/form/{TicketSelectionMap.tsx → tickets/TicketSelectionMap.tsx} +18 -2
  71. package/src/form/tickets/TicketSelectionMobile.tsx +188 -0
  72. package/src/form/{TicketWithMerchandiseSelection.tsx → tickets/TicketWithMerchandiseSelection.tsx} +52 -38
  73. package/src/hooks/useScrollToFirstError.ts +99 -0
  74. package/src/locales/cs.tsx +25 -3
  75. package/src/locales/en.tsx +23 -1
  76. package/src/locales/es.tsx +23 -1
  77. package/src/locales/pl.tsx +23 -1
  78. package/src/locales/sk.tsx +24 -2
  79. package/src/locales/uk.tsx +23 -1
  80. package/src/utils/data/global.ts +1 -0
  81. package/src/utils/data/ticket.ts +1 -0
  82. package/tsconfig.json +1 -1
  83. package/README +0 -1
  84. package/dist/cjs/index-DvUR1fp8.js.map +0 -1
  85. package/dist/cjs/index.umd-6SU6nkkJ.js.map +0 -1
  86. package/dist/esm/index-BlTqx0jm.js.map +0 -1
  87. package/dist/esm/index.umd-Dn0hjh7E.js.map +0 -1
  88. package/src/form/TicketSelection.tsx +0 -307
  89. /package/dist/types/form/{TicketSelection.d.ts → tickets/TicketSelection.d.ts} +0 -0
  90. /package/dist/types/form/{TicketSelectionMap.d.ts → tickets/TicketSelectionMap.d.ts} +0 -0
  91. /package/dist/types/form/{TicketWithMerchandiseSelection.d.ts → tickets/TicketWithMerchandiseSelection.d.ts} +0 -0
@@ -0,0 +1,445 @@
1
+ import { Box, Button, SwipeableDrawer, Typography } from '@mui/material';
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import PaymentOverviewBox from './PaymentOverviewBox';
4
+ import { IEvent } from '@utils/types/event.type';
5
+ import useGlobal from '@hooks/useGlobal';
6
+ import { fCurrency } from '@utils/formatNumber';
7
+ import { EVENTLOOK_ORDER_FORM_ID } from '@utils/data/global';
8
+
9
+ interface Props {
10
+ event: IEvent;
11
+ totalPrice: number;
12
+ termsAndConditionsRef?: React.RefObject<HTMLElement | null>;
13
+ onOpenChange?: (open: boolean) => void;
14
+ }
15
+
16
+ const swipeAreaWidth = 120;
17
+
18
+ const clearResetPulledPastTimeout = (timeoutRef: { current: number | null }) => {
19
+ if (timeoutRef.current !== null) {
20
+ window.clearTimeout(timeoutRef.current);
21
+ timeoutRef.current = null;
22
+ }
23
+ };
24
+
25
+ const PaymentOverviewDrawer: React.FC<Props> = ({
26
+ event,
27
+ totalPrice,
28
+ termsAndConditionsRef,
29
+ onOpenChange,
30
+ }) => {
31
+ const { t, lang } = useGlobal();
32
+ const [isDrawerOpened, setIsDrawerOpened] = useState(false);
33
+ const [isOpeningGesture, setIsOpeningGesture] = useState(false);
34
+ const [hasPulledPastClosedTabHeight, setHasPulledPastClosedTabHeight] = useState(false);
35
+ const [isDrawerHidden, setIsDrawerHidden] = useState(false);
36
+ const [closedTabHeight, setClosedTabHeight] = useState(0);
37
+ const openingTouchStartYRef = useRef<number | null>(null);
38
+ const openDrawerRef = useRef(false);
39
+ const isOpeningGestureRef = useRef(false);
40
+ const shouldReopenOnScrollUpRef = useRef(false);
41
+ const lastScrollYRef = useRef(0);
42
+ const isScrollingDownRef = useRef(false);
43
+ const wasAtPageBottomRef = useRef(false);
44
+ const resetPulledPastTimeoutRef = useRef<number | null>(null);
45
+ const closedTabRef = useRef<HTMLDivElement>(null);
46
+
47
+ useEffect(() => {
48
+ openDrawerRef.current = isDrawerOpened;
49
+ }, [isDrawerOpened]);
50
+
51
+ useEffect(() => {
52
+ isOpeningGestureRef.current = isOpeningGesture;
53
+ }, [isOpeningGesture]);
54
+
55
+ // reopens the drawer when user scrolls up and the drawer was closed by scrolling down to the bottom of the page
56
+ useEffect(() => {
57
+ if (typeof window === 'undefined') {
58
+ return;
59
+ }
60
+
61
+ const atBottomThreshold = 8;
62
+ lastScrollYRef.current = window.scrollY;
63
+ isScrollingDownRef.current = false;
64
+ wasAtPageBottomRef.current =
65
+ window.innerHeight + window.scrollY >=
66
+ document.documentElement.scrollHeight - atBottomThreshold;
67
+
68
+ const handleScroll = () => {
69
+ const currentY = window.scrollY;
70
+ const isScrollingUp = currentY < lastScrollYRef.current;
71
+ const isScrollingDown = currentY > lastScrollYRef.current;
72
+ const isAtPageBottom =
73
+ window.innerHeight + currentY >= document.documentElement.scrollHeight - atBottomThreshold;
74
+
75
+ isScrollingDownRef.current = isScrollingDown;
76
+
77
+ if (
78
+ isScrollingUp &&
79
+ wasAtPageBottomRef.current &&
80
+ shouldReopenOnScrollUpRef.current &&
81
+ !openDrawerRef.current
82
+ ) {
83
+ shouldReopenOnScrollUpRef.current = false;
84
+ setIsDrawerHidden(false);
85
+ openDrawerHandler();
86
+ }
87
+
88
+ wasAtPageBottomRef.current = isAtPageBottom;
89
+ lastScrollYRef.current = currentY;
90
+ };
91
+
92
+ window.addEventListener('scroll', handleScroll, { passive: true });
93
+
94
+ return () => {
95
+ window.removeEventListener('scroll', handleScroll);
96
+ };
97
+ }, []);
98
+
99
+ useEffect(() => {
100
+ const closedTab = closedTabRef.current;
101
+ if (!closedTab) return;
102
+
103
+ const updateClosedTabHeight = () => {
104
+ setClosedTabHeight(closedTab.offsetHeight);
105
+ };
106
+
107
+ updateClosedTabHeight();
108
+
109
+ const resizeObserver = new ResizeObserver(() => {
110
+ updateClosedTabHeight();
111
+ });
112
+
113
+ resizeObserver.observe(closedTab);
114
+
115
+ return () => {
116
+ resizeObserver.disconnect();
117
+ };
118
+ }, []);
119
+
120
+ useEffect(
121
+ () => () => {
122
+ clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
123
+ },
124
+ []
125
+ );
126
+
127
+ useEffect(() => {
128
+ const termsAndConditionsElement = termsAndConditionsRef?.current;
129
+ if (!termsAndConditionsElement) {
130
+ return;
131
+ }
132
+
133
+ if (!('IntersectionObserver' in window)) {
134
+ return;
135
+ }
136
+
137
+ const observer = new IntersectionObserver(
138
+ (entries) => {
139
+ const entry = entries[0];
140
+ const hasReachedTermsSection = entry?.isIntersecting ?? false;
141
+ const shouldAutoCloseDrawer = hasReachedTermsSection;
142
+ const shouldHideDrawerTab =
143
+ hasReachedTermsSection && !openDrawerRef.current && !isOpeningGestureRef.current;
144
+ setIsDrawerHidden(shouldHideDrawerTab);
145
+
146
+ if (shouldAutoCloseDrawer && openDrawerRef.current && isScrollingDownRef.current) {
147
+ closeDrawerHandler({ shouldReopenOnScrollUp: true });
148
+ setIsDrawerHidden(true);
149
+ }
150
+ },
151
+ {
152
+ root: null,
153
+ threshold: 0,
154
+ rootMargin: '0px 0px -40% 0px',
155
+ }
156
+ );
157
+
158
+ observer.observe(termsAndConditionsElement);
159
+
160
+ return () => {
161
+ observer.disconnect();
162
+ };
163
+ }, [termsAndConditionsRef]);
164
+
165
+ const toggleDrawer = () => {
166
+ if (openDrawerRef.current) {
167
+ closeDrawerHandler();
168
+ return;
169
+ }
170
+
171
+ openDrawerHandler();
172
+ };
173
+
174
+ const openDrawerHandler = () => {
175
+ clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
176
+ setIsOpeningGesture(false);
177
+ setHasPulledPastClosedTabHeight(false);
178
+ openingTouchStartYRef.current = null;
179
+ shouldReopenOnScrollUpRef.current = false;
180
+ setIsDrawerOpened(true);
181
+ onOpenChange?.(true);
182
+ };
183
+
184
+ const closeDrawerHandler = ({
185
+ shouldReopenOnScrollUp = false,
186
+ }: { shouldReopenOnScrollUp?: boolean } = {}) => {
187
+ clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
188
+ setIsOpeningGesture(false);
189
+ setHasPulledPastClosedTabHeight(false);
190
+ openingTouchStartYRef.current = null;
191
+ shouldReopenOnScrollUpRef.current = shouldReopenOnScrollUp;
192
+ setIsDrawerOpened(false);
193
+ onOpenChange?.(false);
194
+ };
195
+
196
+ const scheduleResetPulledPastTimeout = () => {
197
+ clearResetPulledPastTimeout(resetPulledPastTimeoutRef);
198
+ resetPulledPastTimeoutRef.current = window.setTimeout(() => {
199
+ if (!openDrawerRef.current) {
200
+ setHasPulledPastClosedTabHeight(false);
201
+ }
202
+ }, 180);
203
+ };
204
+
205
+ const handleGestureStart = (startY: number | null) => {
206
+ openingTouchStartYRef.current = startY;
207
+ setIsOpeningGesture(true);
208
+ setHasPulledPastClosedTabHeight(false);
209
+ };
210
+
211
+ const handleGestureMove = (currentY?: number) => {
212
+ if (!isOpeningGestureRef.current) return;
213
+
214
+ const startY = openingTouchStartYRef.current;
215
+ if (startY === null || typeof currentY !== 'number') return;
216
+
217
+ const pullDistance = startY - currentY;
218
+ const currentClosedTabHeight = closedTabRef.current?.offsetHeight ?? 0;
219
+ setHasPulledPastClosedTabHeight(
220
+ currentClosedTabHeight > 0 && pullDistance >= currentClosedTabHeight
221
+ );
222
+ };
223
+
224
+ const handleGestureCancel = () => {
225
+ openingTouchStartYRef.current = null;
226
+ setIsOpeningGesture(false);
227
+ setHasPulledPastClosedTabHeight(false);
228
+ };
229
+
230
+ const handleGestureEnd = ({
231
+ shouldResetTouchStart = false,
232
+ }: { shouldResetTouchStart?: boolean } = {}) => {
233
+ if (shouldResetTouchStart) {
234
+ openingTouchStartYRef.current = null;
235
+ }
236
+
237
+ if (!isDrawerOpened) {
238
+ setIsOpeningGesture(false);
239
+ scheduleResetPulledPastTimeout();
240
+ }
241
+ };
242
+
243
+ return (
244
+ <Box sx={{ display: { md: 'none' } }}>
245
+ {/* Drawer Tab */}
246
+ <Box
247
+ ref={closedTabRef}
248
+ sx={{
249
+ position: 'fixed',
250
+ bottom: 0,
251
+ left: 0,
252
+ right: 0,
253
+ backgroundColor: (theme) => theme.palette.background.paper,
254
+ borderTopLeftRadius: isOpeningGesture || isDrawerOpened ? 0 : 16,
255
+ borderTopRightRadius: isOpeningGesture || isDrawerOpened ? 0 : 16,
256
+ boxShadow:
257
+ isOpeningGesture || isDrawerOpened ? 'none' : '0 -8px 10px 2px rgba(0,0,0,0.2)',
258
+ zIndex: (theme) => theme.zIndex.drawer + 1,
259
+ opacity: isDrawerHidden ? 0 : 1,
260
+ transform: isDrawerHidden ? 'translateY(calc(100% + 12px))' : 'translateY(0)',
261
+ transition:
262
+ 'opacity 180ms ease, transform 180ms ease, border-radius 120ms ease, box-shadow 120ms ease',
263
+ pointerEvents: 'none',
264
+ }}
265
+ >
266
+ {!isDrawerOpened && (
267
+ <Box
268
+ sx={{
269
+ display: 'flex',
270
+ justifyContent: 'center',
271
+ alignItems: 'center',
272
+ width: '100%',
273
+ py: 1,
274
+ }}
275
+ >
276
+ <Box
277
+ sx={{
278
+ width: 80,
279
+ height: 6,
280
+ backgroundColor: (theme) => theme.palette.grey[300],
281
+ borderRadius: 3,
282
+ opacity: isOpeningGesture ? 0 : 1,
283
+ transition: 'opacity 120ms ease',
284
+ }}
285
+ />
286
+ </Box>
287
+ )}
288
+ <Box
289
+ role="button"
290
+ aria-label={t('form.labels.payment_overview_open')}
291
+ onClick={toggleDrawer}
292
+ onTouchStart={(event: React.TouchEvent<HTMLDivElement>) => {
293
+ handleGestureStart(event.touches[0]?.clientY ?? null);
294
+ }}
295
+ onMouseDown={() => {
296
+ handleGestureStart(null);
297
+ }}
298
+ onTouchMove={(event: React.TouchEvent<HTMLDivElement>) => {
299
+ handleGestureMove(event.touches[0]?.clientY);
300
+ }}
301
+ onTouchCancel={handleGestureCancel}
302
+ onTouchEnd={() => {
303
+ handleGestureEnd({ shouldResetTouchStart: true });
304
+ }}
305
+ onMouseUp={() => {
306
+ handleGestureEnd();
307
+ }}
308
+ sx={{
309
+ width: '100%',
310
+ px: 2,
311
+ pb: 2,
312
+ pointerEvents: isDrawerOpened || isDrawerHidden ? 'none' : 'auto',
313
+ cursor: 'pointer',
314
+ }}
315
+ >
316
+ <Typography
317
+ sx={{
318
+ fontWeight: 'bold',
319
+ display: 'flex',
320
+ justifyContent: 'space-between',
321
+ alignItems: 'center',
322
+ }}
323
+ >
324
+ <span>{t('form.labels.total')}:</span>
325
+ <span>{fCurrency(totalPrice, lang, event.currency)}</span>
326
+ </Typography>
327
+
328
+ <Button
329
+ type="submit"
330
+ form={EVENTLOOK_ORDER_FORM_ID}
331
+ onClick={(event) => {
332
+ event.stopPropagation();
333
+ }}
334
+ variant="contained"
335
+ size="large"
336
+ fullWidth
337
+ sx={{ mt: 1, height: { xs: 36, md: 40 } }}
338
+ >
339
+ {t('buy')}
340
+ </Button>
341
+ </Box>
342
+ </Box>
343
+
344
+ {/* Drawer Content */}
345
+ <SwipeableDrawer
346
+ anchor="bottom"
347
+ open={isDrawerOpened}
348
+ onClose={() => closeDrawerHandler()}
349
+ onOpen={() => openDrawerHandler()}
350
+ swipeAreaWidth={swipeAreaWidth}
351
+ allowSwipeInChildren
352
+ disableSwipeToOpen={isDrawerHidden}
353
+ keepMounted
354
+ SwipeAreaProps={{
355
+ onTouchStart: (event: React.TouchEvent<HTMLDivElement>) => {
356
+ handleGestureStart(event.touches[0]?.clientY ?? null);
357
+ },
358
+ onMouseDown: () => {
359
+ handleGestureStart(null);
360
+ },
361
+ onTouchMove: (event: React.TouchEvent<HTMLDivElement>) => {
362
+ handleGestureMove(event.touches[0]?.clientY);
363
+ },
364
+ onTouchCancel: handleGestureCancel,
365
+ onTouchEnd: () => {
366
+ handleGestureEnd({ shouldResetTouchStart: true });
367
+ },
368
+ onMouseUp: () => {
369
+ handleGestureEnd();
370
+ },
371
+ }}
372
+ sx={{
373
+ display: { md: 'none' },
374
+ pointerEvents: isDrawerOpened ? 'none' : 'auto',
375
+ '& .MuiDrawer-paper': {
376
+ pointerEvents: 'auto',
377
+ },
378
+ '& .MuiSwipeArea-root': {
379
+ pointerEvents: 'auto',
380
+ },
381
+ }}
382
+ ModalProps={{
383
+ disableScrollLock: true,
384
+ keepMounted: false,
385
+ disableAutoFocus: true,
386
+ disableRestoreFocus: true,
387
+ disableEnforceFocus: true,
388
+ BackdropProps: {
389
+ sx: {
390
+ background: 'none',
391
+ pointerEvents: 'none',
392
+ },
393
+ },
394
+ }}
395
+ PaperProps={{
396
+ sx: {
397
+ borderTopLeftRadius: 16,
398
+ borderTopRightRadius: 16,
399
+ boxShadow: '0 -8px 10px 2px rgba(0,0,0,0.2)',
400
+ overflow: 'hidden',
401
+ mb:
402
+ isDrawerOpened ||
403
+ (!isDrawerOpened && (isOpeningGesture || hasPulledPastClosedTabHeight))
404
+ ? `${closedTabHeight}px`
405
+ : 0,
406
+ transition: 'none',
407
+ },
408
+ }}
409
+ >
410
+ <Box
411
+ role="button"
412
+ aria-label={t('form.labels.payment_overview_close')}
413
+ onClick={toggleDrawer}
414
+ sx={{
415
+ display: 'flex',
416
+ justifyContent: 'center',
417
+ alignItems: 'center',
418
+ backgroundColor: (theme) => theme.palette.background.paper,
419
+ cursor: 'pointer',
420
+ py: 1,
421
+ }}
422
+ >
423
+ <Box
424
+ sx={{
425
+ width: 80,
426
+ height: 6,
427
+ backgroundColor: (theme) => theme.palette.grey[300],
428
+ borderRadius: 3,
429
+ }}
430
+ />
431
+ </Box>
432
+ <Box
433
+ sx={{
434
+ height: '100%',
435
+ overflow: 'auto',
436
+ }}
437
+ >
438
+ <PaymentOverviewBox event={event} hideBuyButton />
439
+ </Box>
440
+ </SwipeableDrawer>
441
+ </Box>
442
+ );
443
+ };
444
+
445
+ export default PaymentOverviewDrawer;
@@ -1,4 +1,4 @@
1
- import React, { lazy, Suspense } from 'react';
1
+ import React, { lazy, useEffect, useState } from 'react';
2
2
  import { Box, Typography } from '@mui/material';
3
3
  import bankTransferAnimation from '../utils/data/lottie/bank-transfer.json';
4
4
  import { varBounce } from '@components/animate';
@@ -9,6 +9,23 @@ const Lottie = lazy(() => import('lottie-react'));
9
9
 
10
10
  const PaymentPending: React.FC = () => {
11
11
  const { t } = useGlobal();
12
+ const [LottieComponent, setLottieComponent] = useState<React.ComponentType<any> | null>(null);
13
+
14
+ useEffect(() => {
15
+ if (typeof window === 'undefined') return;
16
+
17
+ let isMounted = true;
18
+
19
+ import('lottie-react').then((module) => {
20
+ if (isMounted) {
21
+ setLottieComponent(() => module.default);
22
+ }
23
+ });
24
+
25
+ return () => {
26
+ isMounted = false;
27
+ };
28
+ }, []);
12
29
 
13
30
  return (
14
31
  <Box textAlign="center">
@@ -18,9 +35,7 @@ const PaymentPending: React.FC = () => {
18
35
  </Typography>
19
36
  </m.div>
20
37
  <Box maxWidth={600} mx="auto">
21
- <Suspense fallback={null}>
22
- <Lottie animationData={bankTransferAnimation} />
23
- </Suspense>
38
+ {LottieComponent ? <LottieComponent animationData={bankTransferAnimation} /> : null}
24
39
  </Box>
25
40
  </Box>
26
41
  );
@@ -52,7 +52,7 @@ const ReleaseWithMerchandise: React.FC<Props> = ({
52
52
  return getSelectedQuantity(releaseId) >= getAvailableTicketsForRelease(release);
53
53
  };
54
54
 
55
- const addRelease = (product?: IEventProductForm) => {
55
+ const addRelease = (variants?: IEventProductForm[]) => {
56
56
  setValue(`tickets.${eventId}`, [
57
57
  ...tickets,
58
58
  {
@@ -60,7 +60,7 @@ const ReleaseWithMerchandise: React.FC<Props> = ({
60
60
  quantity: 1,
61
61
  itemName: '',
62
62
  price: 0,
63
- products: product ? [product] : [],
63
+ products: variants?.length ? variants : [],
64
64
  extraFields: release.extraFields?.length
65
65
  ? [
66
66
  release.extraFields.map((field) => ({
@@ -74,7 +74,7 @@ const ReleaseWithMerchandise: React.FC<Props> = ({
74
74
  setOpenVariantDialog(null);
75
75
  };
76
76
 
77
- const increaseQuantity = (product?: IEventProductForm) => {
77
+ const increaseQuantity = (variants?: IEventProductForm[]) => {
78
78
  const addedRelease = tickets.find((ticket) => ticket.releaseId === release.id);
79
79
  if (addedRelease) {
80
80
  const newQuantity = Number(addedRelease.quantity) + 1;
@@ -86,7 +86,7 @@ const ReleaseWithMerchandise: React.FC<Props> = ({
86
86
  ? {
87
87
  ...ticket,
88
88
  quantity: newQuantity > maxQuantity ? maxQuantity : newQuantity,
89
- products: product ? [...ticket.products, product] : ticket.products,
89
+ products: variants?.length ? [...ticket.products, ...variants] : ticket.products,
90
90
  extraFields: release.extraFields?.length
91
91
  ? [
92
92
  ...ticket.extraFields,
@@ -46,7 +46,6 @@ const Shipping: React.FC<Props> = ({ event }) => {
46
46
  [products]
47
47
  );
48
48
  const shippingMethodId = watch('shipping.shippingMethodId');
49
- const branchId = watch('shipping.branchId');
50
49
  const [displayBranchName, setDisplayBranchName] = useState<string | null>(null);
51
50
  const [firstRender, setFirstRender] = useState<boolean>(true);
52
51
  const filteredShippingMethods = useMemo(
@@ -83,9 +82,21 @@ const Shipping: React.FC<Props> = ({ event }) => {
83
82
  }
84
83
  };
85
84
 
85
+ const openPacketaWidget = () => {
86
+ const interval = setInterval(() => {
87
+ if (window.Packeta && window.Packeta.Widget) {
88
+ clearInterval(interval);
89
+ window.Packeta.Widget.pick(options?.packetaApiKey, onSelectBranch, {
90
+ language: lang,
91
+ });
92
+ }
93
+ }, 100);
94
+ };
95
+
86
96
  const handleChangeBranch = () => {
87
97
  setValue('shipping.branchId', null);
88
98
  setDisplayBranchName(null);
99
+ openPacketaWidget();
89
100
  };
90
101
 
91
102
  useEffect(() => {
@@ -103,25 +114,14 @@ const Shipping: React.FC<Props> = ({ event }) => {
103
114
  if (selectedShippingMethod) {
104
115
  setValue('shipping.price', selectedShippingMethod.price);
105
116
 
106
- if (selectedShippingMethod.type === ShippingTypes.PACKETA) {
107
- if (!branchId) {
108
- const interval = setInterval(() => {
109
- if (window.Packeta && window.Packeta.Widget) {
110
- clearInterval(interval);
111
- window.Packeta.Widget.pick(options?.packetaApiKey, onSelectBranch, {
112
- language: lang,
113
- });
114
- }
115
- }, 100);
116
- }
117
- } else {
117
+ if (selectedShippingMethod.type !== ShippingTypes.PACKETA) {
118
118
  setValue('shipping.branchId', null);
119
119
  setDisplayBranchName(null);
120
120
  }
121
121
  } else {
122
122
  setValue('shipping.price', 0);
123
123
  }
124
- }, [shippingMethodId, shippingMethods, branchId]);
124
+ }, [shippingMethodId, shippingMethods]);
125
125
 
126
126
  return (
127
127
  <>
@@ -141,11 +141,30 @@ const Shipping: React.FC<Props> = ({ event }) => {
141
141
  control={control}
142
142
  render={({ field, fieldState: { error } }) => (
143
143
  <FormControl component="fieldset" sx={{ width: '100%' }}>
144
- <RadioGroup {...field}>
144
+ <RadioGroup
145
+ {...field}
146
+ onChange={(event, value) => {
147
+ field.onChange(event);
148
+ const selectedShippingMethod = filteredShippingMethods.find(
149
+ (method) => method.id === Number(value)
150
+ );
151
+
152
+ if (selectedShippingMethod?.type === ShippingTypes.PACKETA) {
153
+ openPacketaWidget();
154
+ }
155
+ }}
156
+ >
145
157
  {filteredShippingMethods.map((shippingMethod) => (
146
158
  <ShippingMethodItem
147
159
  key={shippingMethod.id}
148
160
  active={Number(shippingMethodId) === shippingMethod.id}
161
+ hasError={!!error}
162
+ sx={{
163
+ '& .MuiFormControlLabel-labelPlacementEnd': {
164
+ mr: 0,
165
+ width: '100%',
166
+ },
167
+ }}
149
168
  >
150
169
  <FormControlLabel
151
170
  value={shippingMethod.id}
@@ -157,35 +176,31 @@ const Shipping: React.FC<Props> = ({ event }) => {
157
176
  alignItems="center"
158
177
  width="100%"
159
178
  >
160
- <Stack direction="row" alignItems="center">
161
- <Box
162
- sx={{
163
- marginRight: 2,
164
- }}
165
- >
179
+ <Stack direction="column">
180
+ <Stack direction="row" alignItems="center" spacing={1}>
166
181
  <Typography sx={{ lineHeight: 1.2 }}>
167
182
  {t(`shipping_method.types.${shippingMethod.type}`)}
168
183
  </Typography>
169
- {displayBranchName &&
170
- shippingMethod.type === ShippingTypes.PACKETA &&
171
- shippingMethod.id === Number(shippingMethodId) && (
172
- <Typography variant="caption" sx={{ lineHeight: 1 }}>
173
- {displayBranchName}
174
- </Typography>
175
- )}
176
- </Box>
177
- {paymentImages[shippingMethod.type]}
184
+ {paymentImages[shippingMethod.type]}
185
+ </Stack>
186
+ {displayBranchName &&
187
+ shippingMethod.type === ShippingTypes.PACKETA &&
188
+ shippingMethod.id === Number(shippingMethodId) && (
189
+ <Typography variant="caption" sx={{ lineHeight: 1 }}>
190
+ {displayBranchName}
191
+ </Typography>
192
+ )}
178
193
  </Stack>
179
194
  {shippingMethod.type === ShippingTypes.PACKETA &&
180
- shippingMethod.id === Number(shippingMethodId) &&
181
- branchId && (
195
+ shippingMethod.id === Number(shippingMethodId) && (
182
196
  <Box>
183
197
  <Button
184
198
  onClick={handleChangeBranch}
185
199
  variant="outlined"
186
200
  size="small"
201
+ sx={{ px: 1, whiteSpace: 'nowrap' }}
187
202
  >
188
- {t('change')}
203
+ {t('event.tickets.shipping.choose_address')}
189
204
  </Button>
190
205
  </Box>
191
206
  )}