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

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 +2 -1
  2. package/.env.example +1 -0
  3. package/README.md +18 -16
  4. package/dist/cjs/{index-DvUR1fp8.js → index-BAfaeq84.js} +3230 -554
  5. package/dist/cjs/index-BAfaeq84.js.map +1 -0
  6. package/dist/cjs/index.js +2 -2
  7. package/dist/cjs/{index.umd-6SU6nkkJ.js → index.umd-Bpwd9vUs.js} +9 -19
  8. package/dist/cjs/index.umd-Bpwd9vUs.js.map +1 -0
  9. package/dist/esm/{index-BlTqx0jm.js → index-CJ_gPli9.js} +3217 -540
  10. package/dist/esm/index-CJ_gPli9.js.map +1 -0
  11. package/dist/esm/index.js +2 -2
  12. package/dist/esm/{index.umd-Dn0hjh7E.js → index.umd-ewNTELOK.js} +9 -19
  13. package/dist/esm/index.umd-ewNTELOK.js.map +1 -0
  14. package/dist/types/components/hook-form/FormProvider.d.ts +2 -1
  15. package/dist/types/form/PaymentOverviewBox.d.ts +2 -0
  16. package/dist/types/form/PaymentOverviewDrawer.d.ts +10 -0
  17. package/dist/types/form/TicketForm.d.ts +1 -0
  18. package/dist/types/form/index.d.ts +2 -1
  19. package/dist/types/form/merchandise/MerchandiseSelection.d.ts +9 -0
  20. package/dist/types/form/merchandise/MerchandiseSlider.d.ts +10 -0
  21. package/dist/types/form/payment/PaymentOverviewCheckbox.d.ts +0 -4
  22. package/dist/types/form/product/ProductVariantsDialog.d.ts +2 -1
  23. package/dist/types/form/services/index.d.ts +7 -0
  24. package/dist/types/form/style.d.ts +1 -0
  25. package/dist/types/form/tickets/ReleaseDescription.d.ts +10 -0
  26. package/dist/types/form/tickets/ReleaseWithMerchandise.d.ts +12 -0
  27. package/dist/types/form/tickets/TicketQuantityControl.d.ts +13 -0
  28. package/dist/types/form/tickets/TicketSelectionMobile.d.ts +17 -0
  29. package/dist/types/hooks/useScrollToFirstError.d.ts +4 -0
  30. package/dist/types/locales/cs.d.ts +22 -0
  31. package/dist/types/locales/en.d.ts +22 -0
  32. package/dist/types/locales/es.d.ts +22 -0
  33. package/dist/types/locales/pl.d.ts +22 -0
  34. package/dist/types/locales/sk.d.ts +22 -0
  35. package/dist/types/locales/uk.d.ts +22 -0
  36. package/dist/types/utils/data/global.d.ts +1 -0
  37. package/package.json +10 -4
  38. package/rollup.config.mjs +7 -12
  39. package/src/components/hook-form/FormProvider.tsx +5 -2
  40. package/src/form/ChildEventDialog.tsx +3 -3
  41. package/src/form/ContactPerson.tsx +1 -1
  42. package/src/form/PaymentOverviewBox.tsx +96 -123
  43. package/src/form/PaymentOverviewDrawer.tsx +446 -0
  44. package/src/form/PaymentPending.tsx +19 -4
  45. package/src/form/ReleaseWithMerchandise.tsx +4 -4
  46. package/src/form/Shipping.tsx +91 -74
  47. package/src/form/TicketForm.tsx +144 -41
  48. package/src/form/index.tsx +3 -1
  49. package/src/form/merchandise/MerchandiseSelection.tsx +24 -0
  50. package/src/form/merchandise/MerchandiseSlider.tsx +62 -0
  51. package/src/form/payment/FeeBox.tsx +4 -31
  52. package/src/form/payment/PaymentOverviewCheckbox.tsx +57 -56
  53. package/src/form/product/ProductCard.tsx +255 -59
  54. package/src/form/product/ProductVariantsDialog.tsx +271 -141
  55. package/src/form/services/index.tsx +263 -0
  56. package/src/form/style.ts +16 -4
  57. package/src/form/tickets/ReleaseDescription.tsx +46 -0
  58. package/src/form/tickets/ReleaseWithMerchandise.tsx +231 -0
  59. package/src/form/tickets/TicketQuantityControl.tsx +100 -0
  60. package/src/form/{TicketSelection.tsx → tickets/TicketSelection.tsx} +24 -128
  61. package/src/form/{TicketSelectionMap.tsx → tickets/TicketSelectionMap.tsx} +9 -1
  62. package/src/form/tickets/TicketSelectionMobile.tsx +177 -0
  63. package/src/form/{TicketWithMerchandiseSelection.tsx → tickets/TicketWithMerchandiseSelection.tsx} +3 -7
  64. package/src/hooks/useScrollToFirstError.ts +99 -0
  65. package/src/locales/cs.tsx +25 -3
  66. package/src/locales/en.tsx +23 -1
  67. package/src/locales/es.tsx +23 -1
  68. package/src/locales/pl.tsx +23 -1
  69. package/src/locales/sk.tsx +24 -2
  70. package/src/locales/uk.tsx +23 -1
  71. package/src/utils/data/global.ts +1 -0
  72. package/tsconfig.json +1 -1
  73. package/README +0 -1
  74. package/dist/cjs/index-DvUR1fp8.js.map +0 -1
  75. package/dist/cjs/index.umd-6SU6nkkJ.js.map +0 -1
  76. package/dist/esm/index-BlTqx0jm.js.map +0 -1
  77. package/dist/esm/index.umd-Dn0hjh7E.js.map +0 -1
  78. /package/dist/types/form/{TicketSelection.d.ts → tickets/TicketSelection.d.ts} +0 -0
  79. /package/dist/types/form/{TicketSelectionMap.d.ts → tickets/TicketSelectionMap.d.ts} +0 -0
  80. /package/dist/types/form/{TicketWithMerchandiseSelection.d.ts → tickets/TicketWithMerchandiseSelection.d.ts} +0 -0
@@ -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
  <>
@@ -136,36 +136,54 @@ const Shipping: React.FC<Props> = ({ event }) => {
136
136
  {t('event.tickets.stepper.5.error')}
137
137
  </Typography>
138
138
  ) : (
139
- <Controller
140
- name="shipping.shippingMethodId"
141
- control={control}
142
- render={({ field, fieldState: { error } }) => (
143
- <FormControl component="fieldset" sx={{ width: '100%' }}>
144
- <RadioGroup {...field}>
145
- {filteredShippingMethods.map((shippingMethod) => (
146
- <ShippingMethodItem
147
- key={shippingMethod.id}
148
- active={Number(shippingMethodId) === shippingMethod.id}
149
- >
150
- <FormControlLabel
151
- value={shippingMethod.id}
152
- control={<Radio />}
153
- label={
154
- <Stack
155
- direction="row"
156
- justifyContent="space-between"
157
- alignItems="center"
158
- width="100%"
159
- >
160
- <Stack direction="row" alignItems="center">
161
- <Box
162
- sx={{
163
- marginRight: 2,
164
- }}
165
- >
166
- <Typography sx={{ lineHeight: 1.2 }}>
167
- {t(`shipping_method.types.${shippingMethod.type}`)}
168
- </Typography>
139
+ <>
140
+ <Controller
141
+ name="shipping.shippingMethodId"
142
+ control={control}
143
+ render={({ field, fieldState: { error } }) => (
144
+ <FormControl component="fieldset" sx={{ width: '100%' }}>
145
+ <RadioGroup
146
+ {...field}
147
+ onChange={(event, value) => {
148
+ field.onChange(event);
149
+ const selectedShippingMethod = filteredShippingMethods.find(
150
+ (method) => method.id === Number(value)
151
+ );
152
+
153
+ if (selectedShippingMethod?.type === ShippingTypes.PACKETA) {
154
+ openPacketaWidget();
155
+ }
156
+ }}
157
+ >
158
+ {filteredShippingMethods.map((shippingMethod) => (
159
+ <ShippingMethodItem
160
+ key={shippingMethod.id}
161
+ active={Number(shippingMethodId) === shippingMethod.id}
162
+ hasError={!!error}
163
+ sx={{
164
+ '& .MuiFormControlLabel-labelPlacementEnd': {
165
+ mr: 0,
166
+ width: '100%',
167
+ },
168
+ }}
169
+ >
170
+ <FormControlLabel
171
+ value={shippingMethod.id}
172
+ control={<Radio />}
173
+ label={
174
+ <Stack
175
+ direction="row"
176
+ justifyContent="space-between"
177
+ alignItems="center"
178
+ width="100%"
179
+ >
180
+ <Stack direction="column">
181
+ <Stack direction="row" alignItems="center" spacing={1}>
182
+ <Typography sx={{ lineHeight: 1.2 }}>
183
+ {t(`shipping_method.types.${shippingMethod.type}`)}
184
+ </Typography>
185
+ {paymentImages[shippingMethod.type]}
186
+ </Stack>
169
187
  {displayBranchName &&
170
188
  shippingMethod.type === ShippingTypes.PACKETA &&
171
189
  shippingMethod.id === Number(shippingMethodId) && (
@@ -173,37 +191,35 @@ const Shipping: React.FC<Props> = ({ event }) => {
173
191
  {displayBranchName}
174
192
  </Typography>
175
193
  )}
176
- </Box>
177
- {paymentImages[shippingMethod.type]}
194
+ </Stack>
195
+ {shippingMethod.type === ShippingTypes.PACKETA &&
196
+ shippingMethod.id === Number(shippingMethodId) && (
197
+ <Box>
198
+ <Button
199
+ onClick={handleChangeBranch}
200
+ variant="outlined"
201
+ size="small"
202
+ sx={{ px: 1, whiteSpace: 'nowrap' }}
203
+ >
204
+ {t('event.tickets.shipping.choose_address')}
205
+ </Button>
206
+ </Box>
207
+ )}
178
208
  </Stack>
179
- {shippingMethod.type === ShippingTypes.PACKETA &&
180
- shippingMethod.id === Number(shippingMethodId) &&
181
- branchId && (
182
- <Box>
183
- <Button
184
- onClick={handleChangeBranch}
185
- variant="outlined"
186
- size="small"
187
- >
188
- {t('change')}
189
- </Button>
190
- </Box>
191
- )}
192
- </Stack>
193
- }
194
- sx={{
195
- '&:not(:last-of-type)': {
196
- mb: 0,
197
- },
198
- '& .MuiFormControlLabel-label': {
199
- width: '100%',
200
- mr: 0,
201
- },
202
- }}
203
- />
204
- </ShippingMethodItem>
205
- ))}
206
- </RadioGroup>
209
+ }
210
+ sx={{
211
+ '&:not(:last-of-type)': {
212
+ mb: 0,
213
+ },
214
+ '& .MuiFormControlLabel-label': {
215
+ width: '100%',
216
+ mr: 0,
217
+ },
218
+ }}
219
+ />
220
+ </ShippingMethodItem>
221
+ ))}
222
+ </RadioGroup>
207
223
 
208
224
  {!!error && (
209
225
  <FormHelperText error={!!error} sx={{ mx: 0 }}>
@@ -213,6 +229,7 @@ const Shipping: React.FC<Props> = ({ event }) => {
213
229
  </FormControl>
214
230
  )}
215
231
  />
232
+ </>
216
233
  )}
217
234
  </>
218
235
  )}
@@ -3,6 +3,7 @@ import PaymentSuccess from '@form/PaymentSuccess';
3
3
  import FormProvider, { RHFCheckbox } from '@components/hook-form';
4
4
  import {
5
5
  Box,
6
+ Divider,
6
7
  Grid,
7
8
  Link,
8
9
  LinkProps,
@@ -14,7 +15,7 @@ import {
14
15
  Typography,
15
16
  } from '@mui/material';
16
17
  import dayjs from 'dayjs';
17
- import TicketSelection from '@form/TicketSelection';
18
+ import TicketSelection from '@form/tickets/TicketSelection';
18
19
  import ContactPerson from '@form/ContactPerson';
19
20
  import Payment from '@form/Payment';
20
21
  import EmailConfirmation from '@form/EmailConfirmation';
@@ -44,18 +45,22 @@ import {
44
45
  import ReleaseCountdown from '@form/ReleaseCountdown';
45
46
  import { cloneObject } from '@utils/global';
46
47
  import PaymentPending from '@form/PaymentPending';
47
- import MerchandiseSelection from '@form/MerchandiseSelection';
48
- import TicketWithMerchandiseSelection from '@form/TicketWithMerchandiseSelection';
48
+ import MerchandiseSelection from '@form/merchandise/MerchandiseSelection';
49
+ import TicketWithMerchandiseSelection from '@form/tickets/TicketWithMerchandiseSelection';
49
50
  import useActiveEventProducts from '@hooks/data/useActiveEventProducts';
50
51
  import Shipping from '@form/Shipping';
51
52
  import useErrors from '@hooks/useErrors';
52
53
  import { EventType } from '@utils/data/event';
53
54
  import TimeslotSelection from '@form/TimeslotSelection';
54
55
  import useGlobal from '@hooks/useGlobal';
56
+ import useScrollToFirstError from '@hooks/useScrollToFirstError';
55
57
  import { Trans } from '@components/Trans';
56
- import { EVENTLOOK_ORDER_FORM_ID } from '@utils/data/global';
58
+ import { EVENTLOOK_ORDER_FORM_ID, EVENTLOOK_ORDER_FORM_CONTAINER_ID } from '@utils/data/global';
57
59
  import ChildEventSection from './ChildEvents';
58
- import TicketSelectionMap from '@form/TicketSelectionMap';
60
+ import TicketSelectionMap from '@form/tickets/TicketSelectionMap';
61
+ import PaymentOverviewDrawer from './PaymentOverviewDrawer';
62
+ import { getPlaceAsString } from '@utils/place';
63
+ import Services from '@form/services';
59
64
 
60
65
  interface Props {
61
66
  event: IEvent;
@@ -63,14 +68,33 @@ interface Props {
63
68
  selectedReleaseId?: number;
64
69
  isIframe?: boolean;
65
70
  isInline?: boolean;
71
+ headerSlot?: React.ReactNode;
66
72
  }
67
73
 
74
+ const getCartUniqueItemCount = (formValues: ITicketForm) => {
75
+ const flatTickets = Object.values(formValues.tickets ?? {}).flat();
76
+ const ticketsCount = flatTickets.reduce(
77
+ (sum, ticket) => sum + (Number(ticket.quantity) > 0 ? 1 : 0),
78
+ 0
79
+ );
80
+ const ticketsWithProductsCount = flatTickets.reduce(
81
+ (sum, ticket) => sum + ((ticket.products?.length ?? 0) > 0 ? 1 : 0),
82
+ 0
83
+ );
84
+ const productsCount = Object.values(formValues.products ?? {})
85
+ .flat()
86
+ .reduce((sum, product) => sum + (Number(product.quantity) || 0), 0);
87
+
88
+ return ticketsCount + ticketsWithProductsCount + productsCount;
89
+ };
90
+
68
91
  const TicketForm: React.FC<Props> = ({
69
92
  event,
70
93
  hasGopayIdSsr,
71
94
  selectedReleaseId,
72
95
  isIframe,
73
96
  isInline,
97
+ headerSlot,
74
98
  }) => {
75
99
  const { t, setGlobal, callbacks, links, user, options, showSnackbar, content, seatingIframeUrl } =
76
100
  useGlobal();
@@ -80,6 +104,7 @@ const TicketForm: React.FC<Props> = ({
80
104
  const [hasGopayId, setHasGopayId] = useState<boolean>(hasGopayIdSsr);
81
105
  const [isPaying, setIsPaying] = useState<boolean>(false);
82
106
  const [formStep, setFormStep] = useState<number>(1);
107
+ const [isPaymentOverviewDrawerOpen, setIsPaymentOverviewDrawerOpen] = useState<boolean>(false);
83
108
  const [showReleaseDate, setShowReleaseDate] = useState(
84
109
  dayjs(event.releaseDate).diff(dayjs()) > 0
85
110
  );
@@ -88,6 +113,8 @@ const TicketForm: React.FC<Props> = ({
88
113
  const hasFiredViewCart = useRef(false);
89
114
  const hasFiredBeginCheckout = useRef(false);
90
115
  const hasFiredPaymentMethod = useRef(false);
116
+ const termsAndConditionsRef = useRef<HTMLDivElement | null>(null);
117
+
91
118
  const item: IEcommerce = {
92
119
  currency: event.currency,
93
120
  items: [
@@ -274,11 +301,36 @@ const TicketForm: React.FC<Props> = ({
274
301
  // }
275
302
  // return true;
276
303
  // }),
277
- shipping: Yup.object().shape({
278
- shippingMethodId: Yup.number().nullable(),
279
- branchId: Yup.string().nullable(),
280
- price: Yup.number(),
281
- }),
304
+ shipping: Yup.object()
305
+ .shape({
306
+ shippingMethodId: Yup.number().nullable(),
307
+ branchId: Yup.string().nullable(),
308
+ price: Yup.number(),
309
+ })
310
+ .test('shipping-method-required', t('form.validation.required'), function (value) {
311
+ if (!event.hasMerchandise) {
312
+ return true;
313
+ }
314
+
315
+ const formValues = this.parent as ITicketForm;
316
+ const hasProducts = Object.values(formValues.products ?? {}).some((arr) => arr.length > 0);
317
+ const allTickets = Object.values(formValues.tickets ?? {}).flat();
318
+ const hasTicketProducts = allTickets.some((ticket) => (ticket.products?.length ?? 0) > 0);
319
+ const requiresShipping = hasProducts || hasTicketProducts;
320
+
321
+ if (!requiresShipping) {
322
+ return true;
323
+ }
324
+
325
+ if (value?.shippingMethodId !== null && value?.shippingMethodId !== undefined) {
326
+ return true;
327
+ }
328
+
329
+ return this.createError({
330
+ path: 'shipping.shippingMethodId',
331
+ message: t('form.validation.required'),
332
+ });
333
+ }),
282
334
  paymentMethodId: Yup.number().nullable().required(t('form.validation.required')),
283
335
  paymentMethodOptionId: Yup.number().nullable(),
284
336
  termsAndConditions: Yup.boolean().isTrue(t('form.validation.terms_and_conditions')),
@@ -306,15 +358,11 @@ const TicketForm: React.FC<Props> = ({
306
358
  defaultValues,
307
359
  });
308
360
  const values = methods.watch();
361
+ const cartItemCount = getCartUniqueItemCount(values);
362
+ const onInvalid = useScrollToFirstError(methods);
309
363
 
310
364
  const onSubmit = async (values: ITicketForm) => {
311
- const allTickets = Object.values(values.tickets).flat();
312
- if (
313
- allTickets.length === 1 &&
314
- !allTickets[0].releaseId &&
315
- !allTickets[0].quantity &&
316
- !values.products.length
317
- ) {
365
+ if (cartItemCount <= 0) {
318
366
  showSnackbar(t('form.validation.count_tickets_or_products'), {
319
367
  variant: 'error',
320
368
  });
@@ -444,7 +492,7 @@ const TicketForm: React.FC<Props> = ({
444
492
  }, [window.location.search]);
445
493
 
446
494
  useEffect(() => {
447
- const subscription = methods.watch((value, { name }) => {
495
+ const subscription = methods.watch((value) => {
448
496
  if (
449
497
  JSON.stringify(defaultValues) !== JSON.stringify(value) &&
450
498
  !hasFiredBeginCheckout.current
@@ -471,7 +519,7 @@ const TicketForm: React.FC<Props> = ({
471
519
  useEffect(() => {
472
520
  if (hasGopayId || isPaying || paymentRedirect) {
473
521
  if (options?.autoscrollAfterViewChange) {
474
- const orderForm = document.getElementById(EVENTLOOK_ORDER_FORM_ID);
522
+ const orderForm = document.getElementById(EVENTLOOK_ORDER_FORM_CONTAINER_ID);
475
523
  if (orderForm) {
476
524
  orderForm.scrollIntoView({ behavior: 'smooth' });
477
525
  }
@@ -483,7 +531,7 @@ const TicketForm: React.FC<Props> = ({
483
531
  return <ReleaseCountdown event={event} setShowReleaseDate={setShowReleaseDate} />;
484
532
 
485
533
  return (
486
- <Box id={EVENTLOOK_ORDER_FORM_ID}>
534
+ <Box id={EVENTLOOK_ORDER_FORM_CONTAINER_ID}>
487
535
  {hasGopayId ? (
488
536
  <PaymentSuccess setIsPaying={setIsPaying} isIframe={isIframe} pixels={pixels} />
489
537
  ) : isPaying ? (
@@ -496,32 +544,65 @@ const TicketForm: React.FC<Props> = ({
496
544
  isInline={isInline}
497
545
  />
498
546
  ) : (
499
- // @ts-ignore -- FormProvider expects stricter form types
500
- <FormProvider methods={methods} onSubmit={methods.handleSubmit(onSubmit)}>
501
- <Typography
547
+ <FormProvider
548
+ methods={methods}
549
+ // @ts-ignore
550
+ onSubmit={methods.handleSubmit(onSubmit, onInvalid)}
551
+ formId={EVENTLOOK_ORDER_FORM_ID}
552
+ >
553
+ <Stack
502
554
  className="overview-card__event-info"
503
555
  display={{ md: 'none' }}
504
- variant="h4"
505
556
  sx={{
506
557
  mb: 2,
507
558
  }}
508
559
  >
509
- {event.name} - {dayjs(event.startDate).format('DD.MM.YYYY HH:mm')}
510
- </Typography>
511
- <Grid container spacing={2}>
560
+ <Typography variant="h3" component="h1">
561
+ {event.name}
562
+ </Typography>
563
+ <Typography variant="h5" component="h2">
564
+ {dayjs(event.startDate).format('DD.MM.YYYY HH:mm')}
565
+ </Typography>
566
+ <Typography variant="body2" mt={1}>
567
+ {getPlaceAsString(event.place)}
568
+ </Typography>
569
+ {headerSlot ? <>{headerSlot}</> : null}
570
+ </Stack>
571
+ <Grid
572
+ container
573
+ spacing={2}
574
+ sx={{
575
+ pb: {
576
+ xs: isPaymentOverviewDrawerOpen ? cartItemCount * 4 + 18 : 0,
577
+ md: 0,
578
+ },
579
+ }}
580
+ >
512
581
  <Grid size={{ xs: 12, md: 8 }}>
513
- <Stepper orientation="vertical">
582
+ <Stepper
583
+ orientation="vertical"
584
+ sx={(theme) => ({
585
+ [theme.breakpoints.down('sm')]: {
586
+ '& .MuiStepContent-root': {
587
+ borderLeftWidth: 0,
588
+ paddingLeft: 0,
589
+ marginLeft: 0,
590
+ },
591
+ '& .MuiStepConnector-line': { borderLeftWidth: 0 },
592
+ },
593
+ })}
594
+ >
514
595
  {event.type === EventType.RECURRING && (
515
596
  <Step active>
516
597
  <StepLabel>{t('event.tickets.stepper.6.title')}</StepLabel>
517
- <StepContent>
598
+ <StepContent sx={{ pr: { xs: 0 } }}>
518
599
  <TimeslotSelection event={event} />
519
600
  </StepContent>
520
601
  </Step>
521
602
  )}
522
603
  <Step active>
523
604
  <StepLabel>{t('event.tickets.stepper.1.title')}</StepLabel>
524
- <StepContent>
605
+ <StepContent sx={{ pr: { xs: 0 } }}>
525
606
  {event.mapId && seatingIframeUrl ? (
526
607
  <TicketSelectionMap event={event} />
527
608
  ) : event.hasMerchandise ? (
@@ -534,7 +615,7 @@ const TicketForm: React.FC<Props> = ({
534
615
  {event.hasMerchandise && eventProducts.length && (
535
616
  <Step active>
536
617
  <StepLabel>{t('event.tickets.stepper.4.title')}</StepLabel>
537
- <StepContent>
618
+ <StepContent sx={{ pr: { xs: 0 } }}>
538
619
  <MerchandiseSelection
539
620
  eventProducts={eventProducts}
540
621
  eventId={event.id}
@@ -543,24 +624,30 @@ const TicketForm: React.FC<Props> = ({
543
624
  </StepContent>
544
625
  </Step>
545
626
  )}
627
+ <Step active>
628
+ <StepLabel>{t('event.tickets.stepper.8.title')}</StepLabel>
629
+ <StepContent sx={{ pr: { xs: 0 } }}>
630
+ <Services event={event} />
631
+ </StepContent>
632
+ </Step>
546
633
  {event.children.length && (
547
634
  <Step active>
548
635
  <StepLabel>{t('event.tickets.stepper.7.title')}</StepLabel>
549
- <StepContent>
636
+ <StepContent sx={{ pr: { xs: 0 } }}>
550
637
  <ChildEventSection events={event.children} />
551
638
  </StepContent>
552
639
  </Step>
553
640
  )}
554
641
  <Step active>
555
642
  <StepLabel>{t('event.tickets.stepper.2.title')}</StepLabel>
556
- <StepContent>
643
+ <StepContent sx={{ pr: { xs: 0 } }}>
557
644
  <ContactPerson event={event} />
558
645
  </StepContent>
559
646
  </Step>
560
647
  {event.hasMerchandise && showShippingMethods() && (
561
648
  <Step active>
562
649
  <StepLabel>{t('event.tickets.stepper.5.title')}</StepLabel>
563
- <StepContent>
650
+ <StepContent sx={{ pr: { xs: 0 } }}>
564
651
  <Shipping event={event} />
565
652
  </StepContent>
566
653
  </Step>
@@ -571,12 +658,17 @@ const TicketForm: React.FC<Props> = ({
571
658
  `event.tickets.stepper.3.${values.isPaymentVerify ? 'title_verify' : 'title'}`
572
659
  )}
573
660
  </StepLabel>
574
- <StepContent>
661
+ <StepContent sx={{ pr: { xs: 0 } }}>
575
662
  <Payment event={event} />
576
663
  </StepContent>
577
664
  </Step>
578
665
  </Stepper>
579
- <Stack mt={2} ml={4}>
666
+ <Stack
667
+ ref={termsAndConditionsRef}
668
+ mt={2}
669
+ ml={{ xs: 1, md: 4 }}
670
+ sx={{ scrollMarginBottom: { xs: 220, md: 0 } }}
671
+ >
580
672
  <RHFCheckbox
581
673
  name="termsAndConditions"
582
674
  label={
@@ -596,15 +688,26 @@ const TicketForm: React.FC<Props> = ({
596
688
  />
597
689
  </Stack>
598
690
  </Grid>
599
- <Grid size={{ xs: 12, md: 4 }}>
600
- <PaymentOverviewBox event={event} />
691
+ <Grid size={12} sx={{ display: { xs: 'block', md: 'none' } }}>
692
+ <Divider sx={{ borderStyle: 'dashed' }} />
693
+ </Grid>
694
+ <Grid size={{ xs: 12, md: 4 }} sx={{ mt: { xs: 0, md: 0 } }}>
695
+ <PaymentOverviewBox event={event} withoutPadding />
601
696
  </Grid>
602
697
  </Grid>
698
+
699
+ <PaymentOverviewDrawer
700
+ event={event}
701
+ totalPrice={values.total}
702
+ termsAndConditionsRef={termsAndConditionsRef}
703
+ onOpenChange={setIsPaymentOverviewDrawerOpen}
704
+ />
705
+
603
706
  <EmailConfirmation
604
707
  open={formStep === 2 && !isIframe}
605
708
  onClose={() => setFormStep(1)}
606
- // @ts-ignore -- handleSubmit return type mismatch with onConfirm prop
607
- onConfirm={methods.handleSubmit(onSubmit)}
709
+ // @ts-ignore
710
+ onConfirm={methods.handleSubmit(onSubmit, onInvalid)}
608
711
  />
609
712
  </FormProvider>
610
713
  )}
@@ -613,7 +716,7 @@ const TicketForm: React.FC<Props> = ({
613
716
  };
614
717
 
615
718
  const CustomLink: React.FC<PropsWithChildren<LinkProps>> = ({ href = '', children, ...other }) => (
616
- <Link href={href} {...other}>
719
+ <Link href={href} {...other} sx={{ color: 'inherit', textDecoration: 'underline' }}>
617
720
  {children}
618
721
  </Link>
619
722
  );
@@ -5,7 +5,7 @@ import 'dayjs/locale/es';
5
5
  import 'dayjs/locale/uk';
6
6
  import 'dayjs/locale/sk';
7
7
 
8
- import React, { useEffect } from 'react';
8
+ import React, { useEffect, ReactNode } from 'react';
9
9
  import TicketForm from '@form/TicketForm';
10
10
  import { GlobalProvider } from '@context/GlobalContext';
11
11
  import api from '@utils/axios';
@@ -33,6 +33,7 @@ export interface OrderFormProps {
33
33
  lang?: Languages;
34
34
  slots?: {
35
35
  showSnackbar: IGlobalContext['showSnackbar'];
36
+ headerSlot?: ReactNode;
36
37
  };
37
38
  user?: IUser;
38
39
  selectedReleaseId?: number;
@@ -112,6 +113,7 @@ const ClientRender: React.FC<OrderFormProps> = ({
112
113
  hasGopayIdSsr={options?.hasGopayId || false}
113
114
  isIframe={options?.isIframe}
114
115
  selectedReleaseId={selectedReleaseId}
116
+ headerSlot={slots?.headerSlot}
115
117
  />
116
118
  )}
117
119
  </GlobalProvider>
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { Grid } from '@mui/material';
3
+ import { IEventProduct } from '@utils/types/event-product.type';
4
+ import CustomSkeleton from '@components/CustomSkeleton';
5
+ import MerchandiseSlider from './MerchandiseSlider';
6
+
7
+ interface Props {
8
+ eventProducts: IEventProduct[];
9
+ eventId: number;
10
+ isLoading?: boolean;
11
+ }
12
+
13
+ const MerchandiseSelection: React.FC<Props> = ({ eventProducts, eventId, isLoading }) =>
14
+ isLoading ? (
15
+ [...Array(3)].map((item) => (
16
+ <Grid key={item} size={{ xs: 12, md: 4 }}>
17
+ <CustomSkeleton height={334} />
18
+ </Grid>
19
+ ))
20
+ ) : (
21
+ <MerchandiseSlider eventProducts={eventProducts} eventId={eventId} />
22
+ );
23
+
24
+ export default MerchandiseSelection;