@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
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import useEmblaCarousel from 'embla-carousel-react';
3
+ import { Box, SxProps } from '@mui/material';
4
+ import { useTheme } from '@mui/material/styles';
5
+ import { IEventProduct } from '@utils/types/event-product.type';
6
+ import ProductCard from '@form/product/ProductCard';
7
+
8
+ interface Props {
9
+ sx?: SxProps;
10
+ eventProducts: IEventProduct[];
11
+ eventId: number;
12
+ }
13
+
14
+ const MerchandiseSlider: React.FC<Props> = ({ eventProducts, eventId, sx }) => {
15
+ const [emblaRef] = useEmblaCarousel();
16
+ const theme = useTheme();
17
+
18
+ return (
19
+ <Box sx={sx}>
20
+ <Box
21
+ sx={{
22
+ overflow: 'hidden',
23
+ padding: theme.spacing(8, 3),
24
+ margin: theme.spacing(-8, -2.5),
25
+ }}
26
+ >
27
+ <Box ref={emblaRef} className="embla" sx={{ overflow: 'visible' }}>
28
+ <Box
29
+ className="embla__container"
30
+ sx={{
31
+ display: 'flex',
32
+ }}
33
+ >
34
+ {eventProducts.map((eventProduct, index) => (
35
+ <Box
36
+ key={`event-slide-${index}`}
37
+ className="embla__slide"
38
+ sx={{
39
+ flex: `0 0 ${eventProducts.length > 1 ? '78%' : '100%'}`,
40
+ minWidth: 0,
41
+ maxWidth: 220,
42
+ margin: `0 ${theme.spacing(0.5)}`,
43
+ transition: 'opacity 0.2s ease',
44
+ [theme.breakpoints.up('sm')]: {
45
+ flex: '0 0 43%',
46
+ },
47
+ [theme.breakpoints.up('md')]: {
48
+ flex: '0 0 33.3333%',
49
+ },
50
+ }}
51
+ >
52
+ <ProductCard eventProduct={eventProduct} eventId={eventId} isOnlyMerchandise />
53
+ </Box>
54
+ ))}
55
+ </Box>
56
+ </Box>
57
+ </Box>
58
+ </Box>
59
+ );
60
+ };
61
+
62
+ export default MerchandiseSlider;
@@ -1,9 +1,7 @@
1
1
  import React from 'react';
2
- import { Box, Stack, Tooltip, Typography } from '@mui/material';
2
+ import { Box, Stack, Typography } from '@mui/material';
3
3
  import { fCurrency } from '@utils/formatNumber';
4
- import { Iconify } from '@components/iconify';
5
4
  import { IEvent } from '@utils/types/event.type';
6
- import useResponsive from '@hooks/useResponsive';
7
5
  import { useFormContext } from 'react-hook-form';
8
6
  import { ITicketForm } from '@utils/types/ticket.type';
9
7
  import useGlobal from '@hooks/useGlobal';
@@ -15,7 +13,6 @@ interface Props {
15
13
 
16
14
  const FeeBox: React.FC<Props> = ({ event, align = 'left' }) => {
17
15
  const { t, lang } = useGlobal();
18
- const xs = useResponsive('only', 'xs');
19
16
  const { watch } = useFormContext<ITicketForm>();
20
17
  const values = watch();
21
18
  const isRight = align === 'right';
@@ -44,35 +41,11 @@ const FeeBox: React.FC<Props> = ({ event, align = 'left' }) => {
44
41
  alignItems="center"
45
42
  spacing={1}
46
43
  >
47
- <Stack direction="row" alignItems="center" spacing={1}>
48
- <Tooltip
49
- title={t('form.labels.price_including_service_fee', {
50
- fee: fCurrency(values.totalFee ?? 0, lang, event.currency),
51
- })}
52
- placement={xs ? 'top' : 'left'}
53
- arrow
54
- sx={{ cursor: 'help' }}
55
- >
56
- <Stack alignItems="center">
57
- <Iconify icon="carbon:information-filled" />
58
- </Stack>
59
- </Tooltip>
60
- <Typography variant="subtitle1" sx={{ textAlign: 'right' }}>
61
- {t('form.labels.total')}:
62
- </Typography>
63
- </Stack>
44
+ <Typography variant="subtitle1" sx={{ textAlign: 'right' }}>
45
+ {t('form.labels.total')}:
46
+ </Typography>
64
47
  <Typography variant="subtitle1">{fCurrency(values.total, lang, event.currency)}</Typography>
65
48
  </Stack>
66
- {!!values.totalFee && (
67
- <Typography
68
- variant="caption"
69
- sx={{
70
- color: (theme) => theme.palette.grey.A700,
71
- }}
72
- >
73
- {t('form.labels.with_fee')}
74
- </Typography>
75
- )}
76
49
  </Box>
77
50
  );
78
51
  };
@@ -1,14 +1,6 @@
1
- import React, { JSX, useState } from 'react';
2
- import {
3
- Dialog,
4
- DialogContent,
5
- DialogTitle,
6
- IconButton,
7
- Link,
8
- Stack,
9
- Typography,
10
- } from '@mui/material';
11
- import { RHFCheckbox } from '@components/hook-form';
1
+ import React, { JSX } from 'react';
2
+ import { Box, Button, Stack, Typography } from '@mui/material';
3
+ import { Controller, useFormContext } from 'react-hook-form';
12
4
  import useGlobal from '@hooks/useGlobal';
13
5
  import { Iconify } from '@components';
14
6
 
@@ -16,70 +8,79 @@ interface Props {
16
8
  checkboxName: string;
17
9
  label: string;
18
10
  value: string | JSX.Element;
19
- modal: {
20
- title: string;
21
- description: string | JSX.Element;
22
- };
23
11
  }
24
12
 
25
- const PaymentOverviewCheckbox: React.FC<Props> = ({ checkboxName, label, value, modal }) => {
13
+ const PaymentOverviewCheckbox: React.FC<Props> = ({ checkboxName, label, value }) => {
26
14
  const { t } = useGlobal();
27
- const [open, setOpen] = useState(false);
28
-
29
- const handleOpen = (e: React.MouseEvent<HTMLAnchorElement>) => {
30
- e.preventDefault();
31
- e.stopPropagation();
32
- setOpen(true);
33
- };
34
- const handleClose = () => setOpen(false);
15
+ const { control } = useFormContext();
35
16
 
36
17
  return (
37
18
  <>
38
- <RHFCheckbox
19
+ <Controller
39
20
  name={checkboxName}
40
- label={
21
+ control={control}
22
+ render={({ field }) => (
41
23
  <Stack
42
24
  direction="row"
43
25
  justifyContent="space-between"
44
26
  alignItems="center"
27
+ useFlexGap
45
28
  spacing={1}
46
- sx={{ width: '100%' }}
29
+ sx={{ width: '100%', mb: 0.5 }}
47
30
  >
48
- <Typography variant="caption" sx={{ flex: 1 }}>
31
+ <Typography variant="body2" sx={{ flex: 1 }}>
49
32
  {label}
50
33
  </Typography>
51
- <Stack alignItems="flex-end">
52
- <Typography variant="caption" fontWeight="600">
34
+ <Box
35
+ sx={{
36
+ position: 'relative',
37
+ display: 'flex',
38
+ alignItems: 'center',
39
+ }}
40
+ >
41
+ <Typography
42
+ variant="body2"
43
+ sx={{
44
+ mr: field.value ? 0 : 1,
45
+ ...(field.value
46
+ ? {
47
+ position: 'absolute',
48
+ right: 0,
49
+ }
50
+ : {}),
51
+ }}
52
+ >
53
53
  {value}
54
54
  </Typography>
55
- <Link variant="caption" onClick={handleOpen}>
56
- {t('more_info')}
57
- </Link>
58
- </Stack>
55
+ <Button
56
+ variant="outlined"
57
+ color="primary"
58
+ size="small"
59
+ startIcon={<Iconify icon="carbon:add" />}
60
+ onClick={(event) => {
61
+ event.preventDefault();
62
+ event.stopPropagation();
63
+ field.onChange(true);
64
+ }}
65
+ sx={{
66
+ borderRadius: 1,
67
+ textTransform: 'none',
68
+ px: 1,
69
+ width: 96,
70
+ minWidth: 96,
71
+ color: 'text.primary',
72
+ visibility: field.value ? 'hidden' : 'visible',
73
+ pointerEvents: field.value ? 'none' : 'auto',
74
+ }}
75
+ tabIndex={field.value ? -1 : 0}
76
+ aria-hidden={field.value}
77
+ >
78
+ {t('add')}
79
+ </Button>
80
+ </Box>
59
81
  </Stack>
60
- }
61
- sx={{
62
- width: 'calc(100% + 11px)',
63
- mb: 0.5,
64
- '& > .MuiTypography-root': {
65
- width: '100%',
66
- },
67
- }}
82
+ )}
68
83
  />
69
-
70
- <Dialog open={open} onClose={handleClose} maxWidth="sm" fullWidth>
71
- <DialogTitle
72
- sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
73
- >
74
- {modal.title}
75
- <IconButton onClick={handleClose} size="small">
76
- <Iconify icon="carbon:close" />
77
- </IconButton>
78
- </DialogTitle>
79
- <DialogContent>
80
- <Typography variant="body2">{modal.description}</Typography>
81
- </DialogContent>
82
- </Dialog>
83
84
  </>
84
85
  );
85
86
  };
@@ -1,6 +1,18 @@
1
- import { Button, Card, Stack, Typography } from '@mui/material';
2
- import { Iconify } from '@components/iconify';
1
+ import {
2
+ Box,
3
+ Button,
4
+ Card,
5
+ Dialog,
6
+ DialogActions,
7
+ DialogContent,
8
+ DialogTitle,
9
+ IconButton,
10
+ Link,
11
+ Stack,
12
+ Typography,
13
+ } from '@mui/material';
3
14
  import Image from '@components/Image';
15
+ import { Iconify } from '@components/iconify';
4
16
  import { IEventProduct } from '@utils/types/event-product.type';
5
17
  import React, { useMemo, useState } from 'react';
6
18
  import ProductVariantsDialog from '@form/product/ProductVariantsDialog';
@@ -9,6 +21,7 @@ import { useFormContext, useWatch } from 'react-hook-form';
9
21
  import { ITicketForm, ITicketFormTicket } from '@utils/types/ticket.type';
10
22
  import { getSelectedQuantityByVariant } from '@utils/product';
11
23
  import useGlobal from '@hooks/useGlobal';
24
+ import { fCurrency } from '@utils/formatNumber';
12
25
 
13
26
  interface Props {
14
27
  eventProduct: IEventProduct;
@@ -17,85 +30,268 @@ interface Props {
17
30
  }
18
31
 
19
32
  const ProductCard: React.FC<Props> = ({ eventProduct, eventId, isOnlyMerchandise }) => {
20
- const { t } = useGlobal();
33
+ const { t, lang } = useGlobal();
21
34
  const [openVariantDialog, setOpenVariantDialog] = useState<boolean>(false);
22
- const { setValue } = useFormContext<ITicketForm>();
35
+ const [openSimpleProductDialog, setOpenSimpleProductDialog] = useState<boolean>(false);
36
+ const { setValue, getValues } = useFormContext<ITicketForm>();
23
37
  const tickets: ITicketFormTicket[] = useWatch({ name: `tickets.${eventId}`, defaultValue: [] });
24
38
  const products: IEventProductForm[] = useWatch({ name: `products.${eventId}`, defaultValue: [] });
39
+ const isSimpleProduct = eventProduct.eventProductVariants.length === 1;
40
+ const simpleVariant = isSimpleProduct ? eventProduct.eventProductVariants[0] : undefined;
25
41
 
26
- const onSelectVariant = (selectedVariant: IEventProductForm) => {
27
- const variant = products.find(
28
- (product) => product.eventProductVariantId === selectedVariant.eventProductVariantId
29
- );
42
+ const onSelectVariant = (selectedVariants: IEventProductForm[] | IEventProductForm) => {
43
+ const normalized = Array.isArray(selectedVariants) ? selectedVariants : [selectedVariants];
44
+ const currentProducts =
45
+ (getValues(`products.${eventId}`) as IEventProductForm[] | undefined) ?? [];
46
+ const nextProducts = [...currentProducts];
47
+
48
+ normalized.forEach((selectedVariant) => {
49
+ const index = nextProducts.findIndex(
50
+ (product) => product.eventProductVariantId === selectedVariant.eventProductVariantId
51
+ );
30
52
 
31
- if (variant) {
32
- if (selectedVariant.quantity === 0) {
33
- setValue(
34
- `products.${eventId}`,
35
- products.filter(
36
- (product) => product.eventProductVariantId !== variant.eventProductVariantId
37
- )
38
- );
53
+ if (index >= 0) {
54
+ if (selectedVariant.quantity <= 0) {
55
+ nextProducts.splice(index, 1);
56
+ } else {
57
+ nextProducts[index] = {
58
+ ...nextProducts[index],
59
+ quantity: selectedVariant.quantity,
60
+ };
61
+ }
39
62
  } else {
40
- setValue(
41
- `products.${eventId}`,
42
- products.map((product) => {
43
- if (product.eventProductVariantId === selectedVariant.eventProductVariantId) {
44
- return {
45
- ...product,
46
- quantity: selectedVariant.quantity,
47
- };
48
- }
49
- return product;
50
- })
51
- );
63
+ if (selectedVariant.quantity > 0) {
64
+ nextProducts.push(selectedVariant);
65
+ }
52
66
  }
53
- } else if (selectedVariant.quantity !== 0) {
54
- setValue(`products.${eventId}`, [...products, selectedVariant]);
55
- }
67
+ });
68
+
69
+ setValue(`products.${eventId}`, nextProducts, {
70
+ shouldDirty: true,
71
+ shouldValidate: true,
72
+ });
56
73
  };
57
74
 
58
- const eventNotEmpty = useMemo(() => products.some((product) => product?.quantity), [products]);
75
+ const eventNotEmpty = useMemo(() => {
76
+ const variantIds = new Set(eventProduct.eventProductVariants.map((variant) => variant.id));
77
+ return products.some(
78
+ (product) => product?.quantity && variantIds.has(product.eventProductVariantId)
79
+ );
80
+ }, [products, eventProduct.eventProductVariants]);
81
+
82
+ const selectedQuantityByVariant = useMemo(
83
+ () => getSelectedQuantityByVariant(products, tickets),
84
+ [products, tickets]
85
+ );
86
+
87
+ const simpleProductQuantity = useMemo(() => {
88
+ if (!simpleVariant) return 0;
89
+ const matched = products.find((product) => product.eventProductVariantId === simpleVariant.id);
90
+ return matched?.quantity ?? 0;
91
+ }, [products, simpleVariant]);
92
+
93
+ const simpleProductMaxAvailable = useMemo(() => {
94
+ if (!simpleVariant) return 0;
95
+ const { quantity, sold } = simpleVariant.productVariant;
96
+ if (!quantity) return Number.POSITIVE_INFINITY;
97
+ const used = selectedQuantityByVariant[simpleVariant.id] ?? 0;
98
+ return quantity - sold - used;
99
+ }, [simpleVariant, selectedQuantityByVariant]);
100
+
101
+ const onChangeSimpleProductQuantity = (nextQuantity: number) => {
102
+ if (!simpleVariant) return;
103
+ if (nextQuantity < 0 || nextQuantity > 10) return;
104
+ if (
105
+ nextQuantity > simpleProductQuantity &&
106
+ nextQuantity - simpleProductQuantity > simpleProductMaxAvailable
107
+ )
108
+ return;
109
+
110
+ onSelectVariant({
111
+ eventProductVariantId: simpleVariant.id,
112
+ productVariantId: simpleVariant.productVariant.id,
113
+ quantity: nextQuantity,
114
+ price: simpleVariant.priceWithVat,
115
+ excludedShippingMethodIds: eventProduct.excludedShippingMethods.map((method) => method.id),
116
+ });
117
+ };
118
+
119
+ const simpleProductQuantityControl =
120
+ simpleProductQuantity > 0 ? (
121
+ <Stack direction="row" spacing={1} alignItems="center" width="100%">
122
+ <IconButton
123
+ onClick={() => onChangeSimpleProductQuantity(simpleProductQuantity - 1)}
124
+ disabled={simpleProductQuantity <= 0}
125
+ sx={{
126
+ flex: '1 1 0',
127
+ height: 36,
128
+ p: 0,
129
+ borderRadius: 1,
130
+ border: '1px solid',
131
+ borderColor: 'grey.300',
132
+ display: 'flex',
133
+ alignItems: 'center',
134
+ justifyContent: 'center',
135
+ }}
136
+ >
137
+ <Iconify icon="eva:minus-fill" width={18} height={18} />
138
+ </IconButton>
139
+ <Box
140
+ sx={{
141
+ flex: '1 1 0',
142
+ height: 36,
143
+ borderRadius: 1,
144
+ border: '1px solid',
145
+ borderColor: 'grey.300',
146
+ display: 'flex',
147
+ alignItems: 'center',
148
+ justifyContent: 'center',
149
+ fontWeight: 700,
150
+ }}
151
+ >
152
+ {simpleProductQuantity}
153
+ </Box>
154
+ <IconButton
155
+ onClick={() => onChangeSimpleProductQuantity(simpleProductQuantity + 1)}
156
+ disabled={simpleProductQuantity >= 10 || simpleProductMaxAvailable <= 0}
157
+ sx={{
158
+ flex: '1 1 0',
159
+ height: 36,
160
+ p: 0,
161
+ borderRadius: 1,
162
+ bgcolor: 'primary.main',
163
+ color: 'primary.contrastText',
164
+ '&:hover': { bgcolor: 'primary.dark' },
165
+ '&.Mui-disabled': {
166
+ bgcolor: 'action.disabledBackground',
167
+ color: 'action.disabled',
168
+ },
169
+ }}
170
+ >
171
+ <Iconify icon="eva:plus-fill" width={18} height={18} />
172
+ </IconButton>
173
+ </Stack>
174
+ ) : (
175
+ <Button
176
+ variant="contained"
177
+ onClick={() => onChangeSimpleProductQuantity(1)}
178
+ disabled={simpleProductMaxAvailable <= 0}
179
+ fullWidth
180
+ >
181
+ {t('add')}
182
+ </Button>
183
+ );
59
184
 
60
185
  return (
61
186
  <>
62
- <Card sx={{ p: 2 }}>
63
- <Stack spacing={1}>
64
- <Image src={eventProduct.product.previewImage.url} ratio="1/1" sx={{ borderRadius: 2 }} />
65
- <Typography variant="h5">{eventProduct.product.name}</Typography>
66
- <Typography
67
- variant="caption"
68
- dangerouslySetInnerHTML={{ __html: eventProduct.product.description }}
69
- sx={{
70
- height: 44,
71
- textOverflow: 'ellipsis',
72
- overflow: 'hidden',
73
- display: '-webkit-box',
74
- WebkitLineClamp: 2,
75
- WebkitBoxOrient: 'vertical',
76
- }}
187
+ <Card
188
+ sx={{
189
+ p: 0,
190
+ borderRadius: 0,
191
+ boxShadow: 'none',
192
+ background: 'none',
193
+ }}
194
+ >
195
+ <Stack spacing={0}>
196
+ <Image
197
+ src={eventProduct.product.previewImage.url}
198
+ ratio="21/9"
199
+ sx={{ borderRadius: 1, maxHeight: 96 }}
77
200
  />
78
- <Button
79
- variant="contained"
80
- onClick={() => setOpenVariantDialog(true)}
81
- fullWidth
82
- startIcon={
83
- eventNotEmpty ? <Iconify icon="eva:edit-fill" /> : <Iconify icon="eva:plus-fill" />
84
- }
85
- >
86
- {eventNotEmpty ? t('change') : t('add')}
87
- </Button>
201
+ <Typography variant="h5" sx={{ fontSize: '14px', fontWeight: 700 }}>
202
+ {eventProduct.product.name}
203
+ </Typography>
204
+ <Stack direction="row" alignItems="center" justifyContent="space-between" spacing={1}>
205
+ <Typography variant="body2" color="text.primary">
206
+ {eventProduct.product.priceWithVat > 0
207
+ ? fCurrency(eventProduct.product.priceWithVat, lang, eventProduct.product.currency)
208
+ : t('free')}
209
+ </Typography>
210
+ {isSimpleProduct && (
211
+ <Link
212
+ onClick={() => setOpenSimpleProductDialog(true)}
213
+ color="inherit"
214
+ underline="always"
215
+ fontSize={12}
216
+ >
217
+ {t('more_info')}
218
+ </Link>
219
+ )}
220
+ </Stack>
221
+ {isSimpleProduct ? (
222
+ simpleProductQuantityControl
223
+ ) : (
224
+ <Button
225
+ variant={eventNotEmpty ? 'contained' : 'outlined'}
226
+ onClick={() => setOpenVariantDialog(true)}
227
+ fullWidth
228
+ sx={{
229
+ color: eventNotEmpty ? (theme) => theme.palette.common.white : 'inherit',
230
+ borderColor: (theme) => theme.palette.grey[300],
231
+ }}
232
+ >
233
+ {eventNotEmpty
234
+ ? t('event.tickets.merchandise.show_added')
235
+ : t('event.tickets.merchandise.show_sizes')}
236
+ </Button>
237
+ )}
88
238
  </Stack>
89
239
  </Card>
90
240
  <ProductVariantsDialog
91
241
  eventProduct={eventProduct}
92
242
  eventId={eventId}
93
- openDialog={openVariantDialog}
243
+ openDialog={openVariantDialog && !isSimpleProduct}
94
244
  onClose={() => setOpenVariantDialog(false)}
95
245
  callback={onSelectVariant}
96
- selectedQuantityByVariant={getSelectedQuantityByVariant(products, tickets)}
246
+ selectedQuantityByVariant={selectedQuantityByVariant}
97
247
  isOnlyMerchandise={isOnlyMerchandise}
98
248
  />
249
+ <Dialog
250
+ open={openSimpleProductDialog && isSimpleProduct}
251
+ onClose={() => setOpenSimpleProductDialog(false)}
252
+ fullWidth
253
+ maxWidth="sm"
254
+ >
255
+ <DialogTitle
256
+ sx={{
257
+ textAlign: 'center',
258
+ fontWeight: 700,
259
+ fontSize: { xs: '1.5rem', sm: '2rem' },
260
+ mb: 2,
261
+ }}
262
+ >
263
+ {eventProduct.product.name}
264
+ </DialogTitle>
265
+ <DialogContent sx={{ pb: 0 }}>
266
+ <Stack spacing={2}>
267
+ <Typography
268
+ variant="body2"
269
+ dangerouslySetInnerHTML={{ __html: eventProduct.product.description }}
270
+ />
271
+ <Typography variant="body1" color="text.primary">
272
+ {eventProduct.product.priceWithVat > 0
273
+ ? fCurrency(eventProduct.product.priceWithVat, lang, eventProduct.product.currency)
274
+ : t('free')}
275
+ </Typography>
276
+ </Stack>
277
+ </DialogContent>
278
+ <DialogActions sx={{ px: 3, pb: 3 }}>
279
+ <Stack spacing={1} width="100%">
280
+ {simpleProductQuantityControl}
281
+ <Button
282
+ variant="outlined"
283
+ onClick={() => setOpenSimpleProductDialog(false)}
284
+ fullWidth
285
+ sx={{
286
+ color: 'inherit',
287
+ borderColor: (theme) => theme.palette.grey[300],
288
+ }}
289
+ >
290
+ {t('close')}
291
+ </Button>
292
+ </Stack>
293
+ </DialogActions>
294
+ </Dialog>
99
295
  </>
100
296
  );
101
297
  };