@eventlook/sdk 1.4.48 → 1.4.49-beta.1

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 (233) hide show
  1. package/.env.example +1 -0
  2. package/dist/cjs/_virtual/_commonjsHelpers.js +8 -0
  3. package/dist/cjs/_virtual/_commonjsHelpers.js.map +1 -0
  4. package/dist/cjs/_virtual/index.js +6 -0
  5. package/dist/cjs/_virtual/index.js.map +1 -0
  6. package/dist/cjs/_virtual/index2.js +6 -0
  7. package/dist/cjs/_virtual/index2.js.map +1 -0
  8. package/dist/cjs/_virtual/index3.js +6 -0
  9. package/dist/cjs/_virtual/index3.js.map +1 -0
  10. package/dist/cjs/_virtual/react-is.development.js +6 -0
  11. package/dist/cjs/_virtual/react-is.development.js.map +1 -0
  12. package/dist/cjs/_virtual/react-is.development2.js +6 -0
  13. package/dist/cjs/_virtual/react-is.development2.js.map +1 -0
  14. package/dist/cjs/_virtual/react-is.production.js +6 -0
  15. package/dist/cjs/_virtual/react-is.production.js.map +1 -0
  16. package/dist/cjs/_virtual/react-is.production.min.js +6 -0
  17. package/dist/cjs/_virtual/react-is.production.min.js.map +1 -0
  18. package/dist/cjs/components/hook-form/FormProvider.js +2 -2
  19. package/dist/cjs/components/hook-form/FormProvider.js.map +1 -1
  20. package/dist/cjs/form/ChildEventDialog.js +3 -3
  21. package/dist/cjs/form/ChildEventDialog.js.map +1 -1
  22. package/dist/cjs/form/ContactPerson.js +1 -1
  23. package/dist/cjs/form/ContactPerson.js.map +1 -1
  24. package/dist/cjs/form/PaymentOverviewBox.js +47 -61
  25. package/dist/cjs/form/PaymentOverviewBox.js.map +1 -1
  26. package/dist/cjs/form/PaymentOverviewDrawer.js +157 -0
  27. package/dist/cjs/form/PaymentOverviewDrawer.js.map +1 -0
  28. package/dist/cjs/form/ReleaseWithMerchandise.js +57 -48
  29. package/dist/cjs/form/ReleaseWithMerchandise.js.map +1 -1
  30. package/dist/cjs/form/Shipping.js +21 -18
  31. package/dist/cjs/form/Shipping.js.map +1 -1
  32. package/dist/cjs/form/TicketForm.js +94 -33
  33. package/dist/cjs/form/TicketForm.js.map +1 -1
  34. package/dist/cjs/form/TicketQuantityControl.js +51 -0
  35. package/dist/cjs/form/TicketQuantityControl.js.map +1 -0
  36. package/dist/cjs/form/TicketSelection.js +5 -6
  37. package/dist/cjs/form/TicketSelection.js.map +1 -1
  38. package/dist/cjs/form/TicketSelectionMobile.js +98 -0
  39. package/dist/cjs/form/TicketSelectionMobile.js.map +1 -0
  40. package/dist/cjs/form/TicketWithMerchandiseSelection.js +3 -5
  41. package/dist/cjs/form/TicketWithMerchandiseSelection.js.map +1 -1
  42. package/dist/cjs/form/index.js +1 -1
  43. package/dist/cjs/form/index.js.map +1 -1
  44. package/dist/cjs/form/merchandise/MerchandiseSelection.js +14 -0
  45. package/dist/cjs/form/merchandise/MerchandiseSelection.js.map +1 -0
  46. package/dist/cjs/form/merchandise/MerchandiseSlider.js +40 -0
  47. package/dist/cjs/form/merchandise/MerchandiseSlider.js.map +1 -0
  48. package/dist/cjs/form/merchendise/MerchandiseSelection.js +19 -0
  49. package/dist/cjs/form/merchendise/MerchandiseSelection.js.map +1 -0
  50. package/dist/cjs/form/merchendise/MerchandiseSlider.js +75 -0
  51. package/dist/cjs/form/merchendise/MerchandiseSlider.js.map +1 -0
  52. package/dist/cjs/form/payment/FeeBox.js +4 -16
  53. package/dist/cjs/form/payment/FeeBox.js.map +1 -1
  54. package/dist/cjs/form/payment/PaymentOverviewCheckbox.js +33 -28
  55. package/dist/cjs/form/payment/PaymentOverviewCheckbox.js.map +1 -1
  56. package/dist/cjs/form/product/ProductCard.js +139 -36
  57. package/dist/cjs/form/product/ProductCard.js.map +1 -1
  58. package/dist/cjs/form/product/ProductVariantsDialog.js +157 -96
  59. package/dist/cjs/form/product/ProductVariantsDialog.js.map +1 -1
  60. package/dist/cjs/form/services/index.js +133 -0
  61. package/dist/cjs/form/services/index.js.map +1 -0
  62. package/dist/cjs/form/style.js +7 -3
  63. package/dist/cjs/form/style.js.map +1 -1
  64. package/dist/cjs/form/tickets/ReleaseDescription.js +23 -0
  65. package/dist/cjs/form/tickets/ReleaseDescription.js.map +1 -0
  66. package/dist/cjs/form/tickets/ReleaseWithMerchandise.js +141 -0
  67. package/dist/cjs/form/tickets/ReleaseWithMerchandise.js.map +1 -0
  68. package/dist/cjs/form/tickets/TicketQuantityControl.js +52 -0
  69. package/dist/cjs/form/tickets/TicketQuantityControl.js.map +1 -0
  70. package/dist/cjs/form/tickets/TicketSelection.js +139 -0
  71. package/dist/cjs/form/tickets/TicketSelection.js.map +1 -0
  72. package/dist/cjs/form/tickets/TicketSelectionMap.js +73 -0
  73. package/dist/cjs/form/tickets/TicketSelectionMap.js.map +1 -0
  74. package/dist/cjs/form/tickets/TicketSelectionMobile.js +90 -0
  75. package/dist/cjs/form/tickets/TicketSelectionMobile.js.map +1 -0
  76. package/dist/cjs/form/tickets/TicketWithMerchandiseSelection.js +117 -0
  77. package/dist/cjs/form/tickets/TicketWithMerchandiseSelection.js.map +1 -0
  78. package/dist/cjs/hooks/useConsentScrollOnDrawerOpen.js +59 -0
  79. package/dist/cjs/hooks/useConsentScrollOnDrawerOpen.js.map +1 -0
  80. package/dist/cjs/hooks/useScrollToFirstError.js +64 -0
  81. package/dist/cjs/hooks/useScrollToFirstError.js.map +1 -0
  82. package/dist/cjs/locales/cs.js +18 -3
  83. package/dist/cjs/locales/cs.js.map +1 -1
  84. package/dist/cjs/locales/en.js +17 -2
  85. package/dist/cjs/locales/en.js.map +1 -1
  86. package/dist/cjs/locales/es.js +16 -1
  87. package/dist/cjs/locales/es.js.map +1 -1
  88. package/dist/cjs/locales/pl.js +16 -1
  89. package/dist/cjs/locales/pl.js.map +1 -1
  90. package/dist/cjs/locales/sk.js +17 -2
  91. package/dist/cjs/locales/sk.js.map +1 -1
  92. package/dist/cjs/locales/uk.js +16 -1
  93. package/dist/cjs/locales/uk.js.map +1 -1
  94. package/dist/cjs/utils/data/global.js +2 -0
  95. package/dist/cjs/utils/data/global.js.map +1 -1
  96. package/dist/esm/_virtual/_commonjsHelpers.js +6 -0
  97. package/dist/esm/_virtual/_commonjsHelpers.js.map +1 -0
  98. package/dist/esm/_virtual/index.js +4 -0
  99. package/dist/esm/_virtual/index.js.map +1 -0
  100. package/dist/esm/_virtual/index2.js +4 -0
  101. package/dist/esm/_virtual/index2.js.map +1 -0
  102. package/dist/esm/_virtual/index3.js +4 -0
  103. package/dist/esm/_virtual/index3.js.map +1 -0
  104. package/dist/esm/_virtual/react-is.development.js +4 -0
  105. package/dist/esm/_virtual/react-is.development.js.map +1 -0
  106. package/dist/esm/_virtual/react-is.development2.js +4 -0
  107. package/dist/esm/_virtual/react-is.development2.js.map +1 -0
  108. package/dist/esm/_virtual/react-is.production.js +4 -0
  109. package/dist/esm/_virtual/react-is.production.js.map +1 -0
  110. package/dist/esm/_virtual/react-is.production.min.js +4 -0
  111. package/dist/esm/_virtual/react-is.production.min.js.map +1 -0
  112. package/dist/esm/components/hook-form/FormProvider.js +2 -2
  113. package/dist/esm/components/hook-form/FormProvider.js.map +1 -1
  114. package/dist/esm/form/ChildEventDialog.js +3 -3
  115. package/dist/esm/form/ChildEventDialog.js.map +1 -1
  116. package/dist/esm/form/ContactPerson.js +1 -1
  117. package/dist/esm/form/ContactPerson.js.map +1 -1
  118. package/dist/esm/form/PaymentOverviewBox.js +48 -62
  119. package/dist/esm/form/PaymentOverviewBox.js.map +1 -1
  120. package/dist/esm/form/PaymentOverviewDrawer.js +153 -0
  121. package/dist/esm/form/PaymentOverviewDrawer.js.map +1 -0
  122. package/dist/esm/form/ReleaseWithMerchandise.js +58 -49
  123. package/dist/esm/form/ReleaseWithMerchandise.js.map +1 -1
  124. package/dist/esm/form/Shipping.js +21 -18
  125. package/dist/esm/form/Shipping.js.map +1 -1
  126. package/dist/esm/form/TicketForm.js +96 -35
  127. package/dist/esm/form/TicketForm.js.map +1 -1
  128. package/dist/esm/form/TicketQuantityControl.js +47 -0
  129. package/dist/esm/form/TicketQuantityControl.js.map +1 -0
  130. package/dist/esm/form/TicketSelection.js +5 -6
  131. package/dist/esm/form/TicketSelection.js.map +1 -1
  132. package/dist/esm/form/TicketSelectionMobile.js +94 -0
  133. package/dist/esm/form/TicketSelectionMobile.js.map +1 -0
  134. package/dist/esm/form/TicketWithMerchandiseSelection.js +4 -6
  135. package/dist/esm/form/TicketWithMerchandiseSelection.js.map +1 -1
  136. package/dist/esm/form/index.js +1 -1
  137. package/dist/esm/form/index.js.map +1 -1
  138. package/dist/esm/form/merchandise/MerchandiseSelection.js +10 -0
  139. package/dist/esm/form/merchandise/MerchandiseSelection.js.map +1 -0
  140. package/dist/esm/form/merchandise/MerchandiseSlider.js +36 -0
  141. package/dist/esm/form/merchandise/MerchandiseSlider.js.map +1 -0
  142. package/dist/esm/form/merchendise/MerchandiseSelection.js +15 -0
  143. package/dist/esm/form/merchendise/MerchandiseSelection.js.map +1 -0
  144. package/dist/esm/form/merchendise/MerchandiseSlider.js +71 -0
  145. package/dist/esm/form/merchendise/MerchandiseSlider.js.map +1 -0
  146. package/dist/esm/form/payment/FeeBox.js +5 -17
  147. package/dist/esm/form/payment/FeeBox.js.map +1 -1
  148. package/dist/esm/form/payment/PaymentOverviewCheckbox.js +35 -30
  149. package/dist/esm/form/payment/PaymentOverviewCheckbox.js.map +1 -1
  150. package/dist/esm/form/product/ProductCard.js +140 -37
  151. package/dist/esm/form/product/ProductCard.js.map +1 -1
  152. package/dist/esm/form/product/ProductVariantsDialog.js +159 -98
  153. package/dist/esm/form/product/ProductVariantsDialog.js.map +1 -1
  154. package/dist/esm/form/services/index.js +129 -0
  155. package/dist/esm/form/services/index.js.map +1 -0
  156. package/dist/esm/form/style.js +7 -3
  157. package/dist/esm/form/style.js.map +1 -1
  158. package/dist/esm/form/tickets/ReleaseDescription.js +19 -0
  159. package/dist/esm/form/tickets/ReleaseDescription.js.map +1 -0
  160. package/dist/esm/form/tickets/ReleaseWithMerchandise.js +137 -0
  161. package/dist/esm/form/tickets/ReleaseWithMerchandise.js.map +1 -0
  162. package/dist/esm/form/tickets/TicketQuantityControl.js +48 -0
  163. package/dist/esm/form/tickets/TicketQuantityControl.js.map +1 -0
  164. package/dist/esm/form/tickets/TicketSelection.js +135 -0
  165. package/dist/esm/form/tickets/TicketSelection.js.map +1 -0
  166. package/dist/esm/form/tickets/TicketSelectionMap.js +69 -0
  167. package/dist/esm/form/tickets/TicketSelectionMap.js.map +1 -0
  168. package/dist/esm/form/tickets/TicketSelectionMobile.js +86 -0
  169. package/dist/esm/form/tickets/TicketSelectionMobile.js.map +1 -0
  170. package/dist/esm/form/tickets/TicketWithMerchandiseSelection.js +113 -0
  171. package/dist/esm/form/tickets/TicketWithMerchandiseSelection.js.map +1 -0
  172. package/dist/esm/hooks/useConsentScrollOnDrawerOpen.js +55 -0
  173. package/dist/esm/hooks/useConsentScrollOnDrawerOpen.js.map +1 -0
  174. package/dist/esm/hooks/useScrollToFirstError.js +60 -0
  175. package/dist/esm/hooks/useScrollToFirstError.js.map +1 -0
  176. package/dist/esm/locales/cs.js +18 -3
  177. package/dist/esm/locales/cs.js.map +1 -1
  178. package/dist/esm/locales/en.js +17 -2
  179. package/dist/esm/locales/en.js.map +1 -1
  180. package/dist/esm/locales/es.js +16 -1
  181. package/dist/esm/locales/es.js.map +1 -1
  182. package/dist/esm/locales/pl.js +16 -1
  183. package/dist/esm/locales/pl.js.map +1 -1
  184. package/dist/esm/locales/sk.js +17 -2
  185. package/dist/esm/locales/sk.js.map +1 -1
  186. package/dist/esm/locales/uk.js +16 -1
  187. package/dist/esm/locales/uk.js.map +1 -1
  188. package/dist/esm/utils/data/global.js +2 -1
  189. package/dist/esm/utils/data/global.js.map +1 -1
  190. package/dist/types/components/Image.d.ts +4 -4
  191. package/dist/types/form/PaymentOverviewDrawer.d.ts +8 -0
  192. package/dist/types/form/merchendise/MerchandiseSelection.d.ts +9 -0
  193. package/dist/types/form/merchendise/MerchandiseSlider.d.ts +10 -0
  194. package/dist/types/form/merchendise/MerchendiseSlider.d.ts +0 -0
  195. package/dist/types/form/style.d.ts +1 -1
  196. package/package.json +5 -1
  197. package/rollup.config.mjs +2 -0
  198. package/src/components/hook-form/FormProvider.tsx +5 -2
  199. package/src/form/ChildEventDialog.tsx +3 -3
  200. package/src/form/ContactPerson.tsx +1 -1
  201. package/src/form/PaymentOverviewBox.tsx +89 -122
  202. package/src/form/PaymentOverviewDrawer.tsx +238 -0
  203. package/src/form/Shipping.tsx +29 -17
  204. package/src/form/TicketForm.tsx +140 -39
  205. package/src/form/index.tsx +3 -1
  206. package/src/form/merchandise/MerchandiseSelection.tsx +24 -0
  207. package/src/form/merchandise/MerchandiseSlider.tsx +62 -0
  208. package/src/form/payment/FeeBox.tsx +4 -31
  209. package/src/form/payment/PaymentOverviewCheckbox.tsx +57 -56
  210. package/src/form/product/ProductCard.tsx +250 -59
  211. package/src/form/product/ProductVariantsDialog.tsx +253 -140
  212. package/src/form/services/index.tsx +263 -0
  213. package/src/form/style.ts +9 -3
  214. package/src/form/tickets/ReleaseDescription.tsx +46 -0
  215. package/src/form/tickets/ReleaseWithMerchandise.tsx +239 -0
  216. package/src/form/tickets/TicketQuantityControl.tsx +94 -0
  217. package/src/form/{TicketSelection.tsx → tickets/TicketSelection.tsx} +24 -128
  218. package/src/form/{TicketSelectionMap.tsx → tickets/TicketSelectionMap.tsx} +9 -1
  219. package/src/form/tickets/TicketSelectionMobile.tsx +177 -0
  220. package/src/form/{TicketWithMerchandiseSelection.tsx → tickets/TicketWithMerchandiseSelection.tsx} +3 -7
  221. package/src/hooks/useConsentScrollOnDrawerOpen.ts +73 -0
  222. package/src/hooks/useScrollToFirstError.ts +94 -0
  223. package/src/locales/cs.tsx +18 -3
  224. package/src/locales/en.tsx +17 -2
  225. package/src/locales/es.tsx +16 -1
  226. package/src/locales/pl.tsx +16 -1
  227. package/src/locales/sk.tsx +17 -2
  228. package/src/locales/uk.tsx +16 -1
  229. package/src/utils/data/global.ts +1 -0
  230. package/tsconfig.json +2 -1
  231. package/.claude/settings.local.json +0 -9
  232. package/src/form/MerchandiseSelection.tsx +0 -29
  233. package/src/form/ReleaseWithMerchandise.tsx +0 -230
@@ -0,0 +1,263 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import {
3
+ Box,
4
+ Button,
5
+ Checkbox,
6
+ Dialog,
7
+ DialogContent,
8
+ DialogTitle,
9
+ Divider,
10
+ FormControlLabel,
11
+ IconButton,
12
+ Link,
13
+ Stack,
14
+ Typography,
15
+ } from '@mui/material';
16
+ import { useWatch, Controller } from 'react-hook-form';
17
+ import { fCurrency } from '@utils/formatNumber';
18
+ import useGlobal from '@hooks/useGlobal';
19
+ import { Iconify } from '@components';
20
+ import { ITicketForm, ITicketFormTicket } from '@utils/types/ticket.type';
21
+ import { IEvent } from '@utils/types/event.type';
22
+ import { useFormContext } from 'react-hook-form';
23
+
24
+ interface Props {
25
+ event: IEvent;
26
+ }
27
+
28
+ interface BorderedCheckboxProps {
29
+ name: string;
30
+ disabled: boolean;
31
+ title: React.ReactNode;
32
+ price: React.ReactNode;
33
+ onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
34
+ }
35
+
36
+ const BorderedCheckbox: React.FC<BorderedCheckboxProps> = ({
37
+ name,
38
+ disabled,
39
+ title,
40
+ price,
41
+ onChange,
42
+ }) => {
43
+ const { control } = useFormContext<ITicketForm>();
44
+
45
+ return (
46
+ <Controller
47
+ name={name as any}
48
+ control={control}
49
+ render={({ field }) => (
50
+ <FormControlLabel
51
+ control={
52
+ <Checkbox
53
+ {...field}
54
+ checked={field.value || false}
55
+ onChange={(e) => {
56
+ onChange?.(e);
57
+ if (!e.defaultPrevented) {
58
+ field.onChange(e);
59
+ }
60
+ }}
61
+ disabled={disabled}
62
+ />
63
+ }
64
+ label={
65
+ <Stack
66
+ direction="row"
67
+ justifyContent="space-between"
68
+ alignItems="center"
69
+ sx={{ width: '100%', pr: 1 }}
70
+ >
71
+ <Typography variant="body2" fontWeight={400}>
72
+ {title}
73
+ </Typography>
74
+ <Typography variant="body2" fontWeight={700} sx={{ whiteSpace: 'nowrap' }}>
75
+ {price}
76
+ </Typography>
77
+ </Stack>
78
+ }
79
+ sx={{
80
+ m: 0,
81
+ px: 1,
82
+ pr: 0.5,
83
+ pl: 0,
84
+ borderRadius: 1,
85
+ width: '100%',
86
+ border: '1px solid',
87
+ borderColor: 'primary.main',
88
+ '& .MuiFormControlLabel-label': {
89
+ width: '100%',
90
+ },
91
+ }}
92
+ />
93
+ )}
94
+ />
95
+ );
96
+ };
97
+
98
+ const Services: React.FC<Props> = ({ event }) => {
99
+ const { t, lang, showSnackbar } = useGlobal();
100
+ const [open, setOpen] = useState(false);
101
+ const ticketInsurancePricePerUnit = Number(
102
+ useWatch<ITicketForm>({
103
+ name: 'ticketInsurancePricePerUnit',
104
+ defaultValue: 0,
105
+ }) || 0
106
+ );
107
+ const smsNotificationPrice = Number(
108
+ useWatch<ITicketForm>({
109
+ name: 'smsNotificationPrice',
110
+ defaultValue: 0,
111
+ }) || 0
112
+ );
113
+ const tickets = useWatch<ITicketForm, 'tickets'>({
114
+ name: 'tickets',
115
+ defaultValue: {},
116
+ });
117
+
118
+ const totalTickets = useMemo(() => {
119
+ const items: ITicketFormTicket[] = Object.values(tickets || {}).flat();
120
+ return items.reduce((sum, ticket) => sum + (Number(ticket.quantity) || 0), 0);
121
+ }, [tickets]);
122
+
123
+ const handleOpen = (e: React.MouseEvent<HTMLAnchorElement>) => {
124
+ e.preventDefault();
125
+ setOpen(true);
126
+ };
127
+
128
+ const handleClose = () => setOpen(false);
129
+
130
+ const handleServiceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
131
+ if (totalTickets <= 0) {
132
+ e.preventDefault();
133
+ showSnackbar(t('event.tickets.services.add_tickets_first'), {
134
+ variant: 'error',
135
+ });
136
+ }
137
+ };
138
+
139
+ return (
140
+ <Stack spacing={1}>
141
+ <BorderedCheckbox
142
+ name="ticketInsurance"
143
+ disabled={false}
144
+ title={t('event.tickets.insurance.label')}
145
+ price={
146
+ <>
147
+ + {fCurrency(ticketInsurancePricePerUnit, lang, event.currency)} /{' '}
148
+ {t('event.tickets.insurance.per_ticket')}
149
+ </>
150
+ }
151
+ onChange={handleServiceChange}
152
+ />
153
+
154
+ <BorderedCheckbox
155
+ name="smsNotification"
156
+ disabled={false}
157
+ title={t('event.tickets.sms_notification.label')}
158
+ price={<>+ {fCurrency(smsNotificationPrice, lang, event.currency)}</>}
159
+ onChange={handleServiceChange}
160
+ />
161
+
162
+ <Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
163
+ <Link variant="body2" underline="always" onClick={handleOpen} color={'text.primary'}>
164
+ {t('event.tickets.stepper.8.additional_info')}
165
+ </Link>
166
+ </Box>
167
+
168
+ <Dialog
169
+ open={open}
170
+ onClose={handleClose}
171
+ maxWidth="sm"
172
+ fullWidth
173
+ PaperProps={{
174
+ sx: {
175
+ borderRadius: 4,
176
+ overflow: 'hidden',
177
+ },
178
+ }}
179
+ >
180
+ <DialogTitle
181
+ sx={{
182
+ position: 'relative',
183
+ textAlign: 'center',
184
+ fontWeight: 700,
185
+ fontSize: { xs: '1.5rem', sm: '1.75rem' },
186
+ pt: { xs: 3.5, sm: 4 },
187
+ pb: { xs: 2, sm: 2.5 },
188
+ px: { xs: 3, sm: 4 },
189
+ }}
190
+ >
191
+ {t('event.tickets.stepper.8.title')}:
192
+ <IconButton
193
+ onClick={handleClose}
194
+ size="small"
195
+ sx={{ position: 'absolute', right: 16, top: 16 }}
196
+ >
197
+ <Iconify icon="carbon:close" />
198
+ </IconButton>
199
+ </DialogTitle>
200
+ <DialogContent sx={{ px: { xs: 3, sm: 4 }, pb: { xs: 4, sm: 5 } }}>
201
+ <Stack spacing={3}>
202
+ <Box>
203
+ <Typography variant="subtitle2" fontWeight={700} gutterBottom>
204
+ {t('event.tickets.insurance.label')}
205
+ </Typography>
206
+ <Typography variant="body2" paragraph>
207
+ {t('event.tickets.insurance.modal.description')}
208
+ </Typography>
209
+ <Typography variant="body2" paragraph>
210
+ {t('event.tickets.insurance.modal.coverage')}
211
+ </Typography>
212
+ <BorderedCheckbox
213
+ name="ticketInsurance"
214
+ disabled={false}
215
+ title={t('event.tickets.insurance.label')}
216
+ price={
217
+ <>
218
+ + {fCurrency(ticketInsurancePricePerUnit, lang, event.currency)} /{' '}
219
+ {t('event.tickets.insurance.per_ticket')}
220
+ </>
221
+ }
222
+ onChange={handleServiceChange}
223
+ />
224
+ </Box>
225
+ <Divider />
226
+ <Box>
227
+ <Typography variant="subtitle2" fontWeight={700} gutterBottom>
228
+ {t('event.tickets.sms_notification.label')}
229
+ </Typography>
230
+ <Typography variant="body2" paragraph>
231
+ {t('event.tickets.sms_notification.modal.description')}
232
+ </Typography>
233
+ <BorderedCheckbox
234
+ name="smsNotification"
235
+ disabled={false}
236
+ title={t('event.tickets.sms_notification.label')}
237
+ price={<>+ {fCurrency(smsNotificationPrice, lang, event.currency)}</>}
238
+ onChange={handleServiceChange}
239
+ />
240
+ </Box>
241
+ <Button
242
+ variant="outlined"
243
+ onClick={handleClose}
244
+ sx={{
245
+ mt: 1,
246
+ py: 1.25,
247
+ borderRadius: 1,
248
+ height: 36,
249
+ fontWeight: 700,
250
+ borderColor: 'grey.300',
251
+ color: 'text.primary',
252
+ }}
253
+ >
254
+ {t('close')}
255
+ </Button>
256
+ </Stack>
257
+ </DialogContent>
258
+ </Dialog>
259
+ </Stack>
260
+ );
261
+ };
262
+
263
+ export default Services;
package/src/form/style.ts CHANGED
@@ -9,10 +9,16 @@ export const OverviewCard = styled(Card)<{ stickyHeaderTop: number }>(
9
9
  );
10
10
 
11
11
  export const ShippingMethodItem = styled(Box, {
12
- shouldForwardProp: (prop) => prop !== 'active',
13
- })<{ active: boolean }>(({ theme, active }) => ({
12
+ shouldForwardProp: (prop) => prop !== 'active' && prop !== 'hasError',
13
+ })<{ active: boolean; hasError?: boolean }>(({ theme, active, hasError }) => ({
14
14
  borderRadius: theme.spacing(1),
15
- border: `1px solid ${active ? theme.palette.primary.main : theme.palette.grey.A200}`,
15
+ border: `1px solid ${
16
+ hasError
17
+ ? theme.palette.error.main
18
+ : active
19
+ ? theme.palette.primary.main
20
+ : theme.palette.grey.A200
21
+ }`,
16
22
  marginTop: theme.spacing(1),
17
23
  width: '100%',
18
24
  padding: `0 ${theme.spacing(2)}`,
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { Box, Collapse, Link, Stack, Typography } from '@mui/material';
3
+ import { Iconify } from '@components/iconify';
4
+
5
+ interface ReleaseDescriptionProps {
6
+ description?: string | null;
7
+ isExpanded: boolean;
8
+ onToggle: () => void;
9
+ moreInfoLabel: string;
10
+ showCollapse?: boolean;
11
+ }
12
+
13
+ const ReleaseDescription: React.FC<ReleaseDescriptionProps> = ({
14
+ description,
15
+ isExpanded,
16
+ onToggle,
17
+ moreInfoLabel,
18
+ showCollapse = false,
19
+ }) => {
20
+ if (!description) return null;
21
+
22
+ if (showCollapse) {
23
+ return (
24
+ <Collapse in={isExpanded}>
25
+ <Typography variant="body2" color="text.secondary">
26
+ {description}
27
+ </Typography>
28
+ </Collapse>
29
+ );
30
+ }
31
+
32
+ return (
33
+ <Link onClick={onToggle} color="inherit" underline="always" fontSize={12}>
34
+ <Stack direction="row" alignItems="center" spacing={0}>
35
+ <Box>{moreInfoLabel}</Box>
36
+
37
+ <Iconify
38
+ sx={{ width: 24, height: 24 }}
39
+ icon={isExpanded ? 'eva:chevron-up-fill' : 'eva:chevron-down-fill'}
40
+ />
41
+ </Stack>
42
+ </Link>
43
+ );
44
+ };
45
+
46
+ export default ReleaseDescription;
@@ -0,0 +1,239 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { Box, Stack, Typography } from '@mui/material';
3
+ import ProductVariantsDialog from '@form/product/ProductVariantsDialog';
4
+ import TicketQuantityControl from '@form/tickets/TicketQuantityControl';
5
+ import { IReleaseShort } from '@utils/types/release.type';
6
+ import { ITicketForm, ITicketFormTicket } from '@utils/types/ticket.type';
7
+ import { useFormContext, useWatch } from 'react-hook-form';
8
+ import { IEventProductForm } from '@utils/types/product.type';
9
+ import { fCurrency } from '@utils/formatNumber';
10
+ import { Currencies } from '@utils/data/currency';
11
+ import { getSelectedQuantityByVariant } from '@utils/product';
12
+ import ReleaseExtraFields from '@form/extra-field/ReleaseExtraFields';
13
+ import ReleaseDescription from '@form/tickets/ReleaseDescription';
14
+ import useGlobal from '@hooks/useGlobal.ts';
15
+
16
+ interface Props {
17
+ eventId: number;
18
+ release: IReleaseShort;
19
+ activeReleases: IReleaseShort[];
20
+ currency: Currencies;
21
+ index: number;
22
+ }
23
+
24
+ const ReleaseWithMerchandise: React.FC<Props> = ({
25
+ eventId,
26
+ release,
27
+ activeReleases,
28
+ currency,
29
+ index,
30
+ }) => {
31
+ const { t, lang } = useGlobal();
32
+ const [openVariantDialog, setOpenVariantDialog] = useState<'add' | 'increase' | null>(null);
33
+ const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false);
34
+ const { setValue } = useFormContext<ITicketForm>();
35
+ const tickets: ITicketFormTicket[] = useWatch({ name: `tickets.${eventId}`, defaultValue: [] });
36
+ const products: IEventProductForm[] = useWatch({ name: `products.${eventId}`, defaultValue: [] });
37
+ const addedRelease = tickets.find((ticket) => ticket.releaseId === release.id);
38
+ const countTickets = addedRelease?.quantity || 0;
39
+
40
+ const getReleaseTitle = (release: IReleaseShort) =>
41
+ release.releaseCategoryName || release.name || '';
42
+
43
+ const getSelectedQuantity = (id: number) =>
44
+ tickets.find((ticket) => ticket.releaseId === id)?.quantity || 0;
45
+
46
+ const getAvailableTicketsForRelease = (release: ITicketFormTicket): number => {
47
+ const selectedRelease = activeReleases?.find((item) => item.id === release.releaseId);
48
+ const availableQuantity = selectedRelease ? selectedRelease.availableTickets : 0;
49
+ return availableQuantity > 10 ? 10 : availableQuantity;
50
+ };
51
+
52
+ const isMaxQuantity = (releaseId: number) => {
53
+ const release = tickets.find((ticket) => ticket.releaseId === releaseId);
54
+ if (!release) return false;
55
+ return getSelectedQuantity(releaseId) >= getAvailableTicketsForRelease(release);
56
+ };
57
+
58
+ const addRelease = (productsToAdd?: IEventProductForm[] | IEventProductForm) => {
59
+ const normalizedProducts = Array.isArray(productsToAdd)
60
+ ? productsToAdd
61
+ : productsToAdd
62
+ ? [productsToAdd]
63
+ : [];
64
+ const quantity = normalizedProducts.length ? normalizedProducts.length : 1;
65
+ const extraFields = release.extraFields?.length
66
+ ? Array.from({ length: quantity }, () =>
67
+ release.extraFields!.map((field) => ({
68
+ eventExtraFieldId: field.id,
69
+ value: '',
70
+ }))
71
+ )
72
+ : [];
73
+
74
+ setValue(`tickets.${eventId}`, [
75
+ ...tickets,
76
+ {
77
+ releaseId: release.id,
78
+ quantity,
79
+ itemName: '',
80
+ price: 0,
81
+ products: normalizedProducts,
82
+ extraFields,
83
+ },
84
+ ]);
85
+ setOpenVariantDialog(null);
86
+ };
87
+
88
+ const increaseQuantity = (productsToAdd?: IEventProductForm[] | IEventProductForm) => {
89
+ const normalizedProducts = Array.isArray(productsToAdd)
90
+ ? productsToAdd
91
+ : productsToAdd
92
+ ? [productsToAdd]
93
+ : [];
94
+ const addedRelease = tickets.find((ticket) => ticket.releaseId === release.id);
95
+ if (addedRelease) {
96
+ const increment = normalizedProducts.length ? normalizedProducts.length : 1;
97
+ const maxQuantity = getAvailableTicketsForRelease(addedRelease);
98
+ const availableIncrement = Math.max(
99
+ 0,
100
+ Math.min(increment, maxQuantity - Number(addedRelease.quantity))
101
+ );
102
+ if (availableIncrement === 0) return;
103
+
104
+ const newQuantity = Number(addedRelease.quantity) + availableIncrement;
105
+ const productsSlice = normalizedProducts.slice(0, availableIncrement);
106
+ const extraFieldsToAdd = release.extraFields?.length
107
+ ? Array.from({ length: availableIncrement }, () =>
108
+ release.extraFields!.map((field) => ({
109
+ eventExtraFieldId: field.id,
110
+ value: '',
111
+ }))
112
+ )
113
+ : [];
114
+
115
+ setValue(
116
+ `tickets.${eventId}`,
117
+ tickets.map((ticket) =>
118
+ ticket.releaseId === release.id
119
+ ? {
120
+ ...ticket,
121
+ quantity: newQuantity > maxQuantity ? maxQuantity : newQuantity,
122
+ products: [...ticket.products, ...productsSlice],
123
+ extraFields: release.extraFields?.length
124
+ ? [...ticket.extraFields, ...extraFieldsToAdd]
125
+ : [],
126
+ }
127
+ : ticket
128
+ )
129
+ );
130
+ setOpenVariantDialog(null);
131
+ }
132
+ };
133
+
134
+ const decreaseQuantity = useCallback(() => {
135
+ const addedRelease = tickets.find((ticket) => ticket.releaseId === release.id);
136
+ if (addedRelease) {
137
+ const newQuantity = Number(addedRelease.quantity) - 1;
138
+ if (newQuantity < 1) {
139
+ setValue(
140
+ `tickets.${eventId}`,
141
+ tickets.filter((ticket) => ticket.releaseId !== release.id)
142
+ );
143
+ } else {
144
+ setValue(
145
+ `tickets.${eventId}`,
146
+ tickets.map((ticket) => {
147
+ if (ticket.releaseId !== release.id) return ticket;
148
+
149
+ return {
150
+ ...ticket,
151
+ quantity: newQuantity,
152
+ products: ticket?.products?.slice(0, -1), // non-mutating "pop"
153
+ extraFields: ticket?.extraFields?.slice(0, -1),
154
+ };
155
+ })
156
+ );
157
+ }
158
+ }
159
+ }, [tickets, release.id, setValue]);
160
+
161
+ return (
162
+ <Box
163
+ sx={{
164
+ pt: 1,
165
+ pr: 0.5,
166
+ pb: 0.5,
167
+ pl: 2,
168
+ borderRadius: 1,
169
+ bgcolor: (theme) =>
170
+ theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[800],
171
+ }}
172
+ >
173
+ <Stack spacing={0}>
174
+ <Box>
175
+ <Typography variant="subtitle2" fontWeight={700}>
176
+ {getReleaseTitle(release)}
177
+ </Typography>
178
+ </Box>
179
+
180
+ <Stack direction="row" alignItems="center" justifyContent="space-between">
181
+ <Stack>
182
+ <Typography variant="body2">
183
+ {release.price === 0 ? t('free') : fCurrency(release.price, lang, currency)}
184
+ </Typography>
185
+
186
+ <ReleaseDescription
187
+ description={release.description}
188
+ isExpanded={isDescriptionExpanded}
189
+ onToggle={() => setIsDescriptionExpanded((prev) => !prev)}
190
+ moreInfoLabel={t('more_info')}
191
+ />
192
+ </Stack>
193
+
194
+ <TicketQuantityControl
195
+ quantity={getSelectedQuantity(release.id)}
196
+ isDisabled={release.locked}
197
+ canAddFirst={!release.locked}
198
+ canAddMore={!isMaxQuantity(release.id)}
199
+ addLabel={t('add')}
200
+ onDecrement={() => decreaseQuantity()}
201
+ onIncrement={() =>
202
+ release.product ? setOpenVariantDialog('increase') : increaseQuantity()
203
+ }
204
+ onAddFirst={() => (release.product ? setOpenVariantDialog('add') : addRelease())}
205
+ />
206
+ </Stack>
207
+
208
+ <ReleaseDescription
209
+ description={release.description}
210
+ isExpanded={isDescriptionExpanded}
211
+ onToggle={() => setIsDescriptionExpanded((prev) => !prev)}
212
+ moreInfoLabel={t('more_info')}
213
+ showCollapse
214
+ />
215
+ {release.extraFields && release.extraFields.length > 0 && (
216
+ <ReleaseExtraFields
217
+ release={release}
218
+ eventId={eventId}
219
+ releaseIndex={index}
220
+ quantity={countTickets}
221
+ />
222
+ )}
223
+ {release.product && (
224
+ <ProductVariantsDialog
225
+ eventProduct={release.product}
226
+ openDialog={!!openVariantDialog}
227
+ callback={openVariantDialog === 'increase' ? increaseQuantity : addRelease}
228
+ onClose={() => setOpenVariantDialog(null)}
229
+ selectedQuantityByVariant={getSelectedQuantityByVariant(products, tickets)}
230
+ eventId={eventId}
231
+ canAddOnlyOneAtATime
232
+ />
233
+ )}
234
+ </Stack>
235
+ </Box>
236
+ );
237
+ };
238
+
239
+ export default ReleaseWithMerchandise;
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import { Box, Button, IconButton, Stack } from '@mui/material';
3
+ import { Iconify } from '@components/iconify';
4
+
5
+ interface TicketQuantityControlProps {
6
+ quantity: number;
7
+ isDisabled: boolean;
8
+ canAddFirst: boolean;
9
+ canAddMore: boolean;
10
+ addLabel: string;
11
+ onDecrement: () => void;
12
+ onIncrement: () => void;
13
+ onAddFirst: () => void;
14
+ }
15
+
16
+ const TicketQuantityControl: React.FC<TicketQuantityControlProps> = ({
17
+ quantity,
18
+ isDisabled,
19
+ canAddFirst,
20
+ canAddMore,
21
+ addLabel,
22
+ onDecrement,
23
+ onIncrement,
24
+ onAddFirst,
25
+ }) => {
26
+ if (quantity > 0) {
27
+ return (
28
+ <Stack direction="row" spacing={{ xs: 0.5, md: 1 }} alignItems="center">
29
+ <IconButton
30
+ onClick={onDecrement}
31
+ disabled={isDisabled || quantity <= 0}
32
+ sx={{
33
+ width: { xs: 36, md: 40 },
34
+ height: { xs: 36, md: 40 },
35
+ borderRadius: 1,
36
+ border: '1px solid',
37
+ borderColor: 'grey.300',
38
+ }}
39
+ >
40
+ <Iconify icon="eva:minus-fill" />
41
+ </IconButton>
42
+ <Box
43
+ sx={{
44
+ width: { xs: 36, md: 40 },
45
+ height: { xs: 36, md: 40 },
46
+ borderRadius: 1,
47
+ border: '1px solid',
48
+ borderColor: 'grey.300',
49
+ display: 'flex',
50
+ alignItems: 'center',
51
+ justifyContent: 'center',
52
+ fontWeight: 600,
53
+ }}
54
+ >
55
+ {quantity}
56
+ </Box>
57
+ <IconButton
58
+ onClick={onIncrement}
59
+ disabled={isDisabled || !canAddMore}
60
+ sx={{
61
+ width: { xs: 36, md: 40 },
62
+ height: { xs: 36, md: 40 },
63
+ borderRadius: 1,
64
+ bgcolor: 'primary.main',
65
+ color: 'primary.contrastText',
66
+ '&:hover': { bgcolor: 'primary.dark' },
67
+ }}
68
+ >
69
+ <Iconify icon="eva:plus-fill" />
70
+ </IconButton>
71
+ </Stack>
72
+ );
73
+ }
74
+
75
+ return (
76
+ <Button
77
+ variant="contained"
78
+ onClick={onAddFirst}
79
+ disabled={isDisabled || !canAddFirst}
80
+ sx={{
81
+ height: { xs: 36, md: 40 },
82
+ width: { xs: 116, md: 136 },
83
+ borderRadius: 1,
84
+ px: 3,
85
+ textTransform: 'none',
86
+ fontWeight: 600,
87
+ }}
88
+ >
89
+ {addLabel}
90
+ </Button>
91
+ );
92
+ };
93
+
94
+ export default TicketQuantityControl;