@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
@@ -1,12 +1,11 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
2
  import {
3
- Badge,
3
+ Box,
4
4
  Button,
5
5
  Dialog,
6
6
  DialogActions,
7
7
  DialogContent,
8
8
  DialogTitle,
9
- IconButton,
10
9
  Stack,
11
10
  Typography,
12
11
  } from '@mui/material';
@@ -16,17 +15,19 @@ import { useWatch } from 'react-hook-form';
16
15
  import { ITicketFormTicket } from '@utils/types/ticket.type';
17
16
  import { IEventProductForm } from '@utils/types/product.type';
18
17
  import { fCurrency } from '@utils/formatNumber';
18
+ import { MAX_TICKETS_PER_ORDER } from '@utils/data/ticket';
19
19
  import useGlobal from '@hooks/useGlobal';
20
- import { Iconify } from '@components';
21
20
 
22
21
  interface Props {
23
22
  eventProduct: IEventProduct;
24
23
  eventId: number;
25
24
  openDialog: boolean;
26
- callback: (variant: IEventProductForm) => void;
25
+ callback: (variants: IEventProductForm[]) => void;
27
26
  onClose?: () => void;
28
27
  selectedQuantityByVariant?: Record<number, number>;
29
28
  isOnlyMerchandise?: boolean;
29
+ canAddOnlyOneAtATime?: boolean;
30
+ maxSelectableQuantity?: number;
30
31
  }
31
32
 
32
33
  const ProductVariantsDialog: React.FC<Props> = ({
@@ -37,13 +38,12 @@ const ProductVariantsDialog: React.FC<Props> = ({
37
38
  selectedQuantityByVariant,
38
39
  isOnlyMerchandise,
39
40
  eventId,
41
+ canAddOnlyOneAtATime,
42
+ maxSelectableQuantity,
40
43
  }) => {
41
44
  const { t, lang, options } = useGlobal();
42
45
  const { showSnackbar } = useGlobal();
43
- const [selectedVariant, setSelectedVariant] = useState<IEventProductForm | null>(null);
44
- const [variantPrice, setVariantPrice] = useState<number | null>(null);
45
- const [variantQuantity, setVariantQuantity] = useState<number>(1);
46
- const [isVariantClicked, setIsVariantClicked] = useState(false);
46
+ const [draftQuantities, setDraftQuantities] = useState<Record<number, number>>({});
47
47
  const tickets: ITicketFormTicket[] = useWatch({ name: `tickets.${eventId}`, defaultValue: [] });
48
48
  const products: IEventProductForm[] = useWatch({ name: `products.${eventId}`, defaultValue: [] });
49
49
 
@@ -56,43 +56,51 @@ const ProductVariantsDialog: React.FC<Props> = ({
56
56
  }, [openDialog]);
57
57
 
58
58
  useEffect(() => {
59
- if (isVariantClicked) {
60
- setVariantQuantity(
61
- selectedQuantityByVariant &&
62
- selectedVariant &&
63
- selectedQuantityByVariant[selectedVariant.eventProductVariantId] !== undefined
64
- ? selectedQuantityByVariant[selectedVariant.eventProductVariantId]
65
- : 1
66
- );
67
- setIsVariantClicked(false);
59
+ if (openDialog && canAddOnlyOneAtATime) {
60
+ setDraftQuantities({});
68
61
  }
69
- }, [selectedVariant, isVariantClicked]);
62
+ }, [openDialog, canAddOnlyOneAtATime]);
70
63
 
71
- const handleOnClick = (variant: IEventProductVariant) => {
72
- setVariantPrice(variant.priceWithVat);
73
- setSelectedVariant({
74
- eventProductVariantId: variant.id,
75
- productVariantId: variant.productVariant.id,
76
- quantity: 1,
77
- price: variant.priceWithVat,
78
- excludedShippingMethodIds: eventProduct.excludedShippingMethods.map((method) => method.id),
79
- });
80
- setIsVariantClicked(true);
81
- };
64
+ useEffect(() => {
65
+ if (openDialog && !canAddOnlyOneAtATime) {
66
+ setDraftQuantities(selectedQuantityByVariant ?? {});
67
+ }
68
+ }, [openDialog, selectedQuantityByVariant, canAddOnlyOneAtATime]);
82
69
 
83
70
  const handleOnAdd = () => {
84
- if (!selectedVariant) {
71
+ const selectedVariants = eventProduct.eventProductVariants
72
+ .map((variant) => {
73
+ const quantity = draftQuantities[variant.id] ?? 0;
74
+ const selectedQty = selectedQuantityByVariant?.[variant.id] ?? 0;
75
+ if (canAddOnlyOneAtATime) {
76
+ if (quantity <= 0) return null;
77
+ } else if (quantity <= 0 && selectedQty <= 0) {
78
+ return null;
79
+ }
80
+ return {
81
+ eventProductVariantId: variant.id,
82
+ productVariantId: variant.productVariant.id,
83
+ quantity,
84
+ price: variant.priceWithVat,
85
+ excludedShippingMethodIds: eventProduct.excludedShippingMethods.map(
86
+ (method) => method.id
87
+ ),
88
+ } as IEventProductForm;
89
+ })
90
+ .filter((variant): variant is IEventProductForm => Boolean(variant));
91
+
92
+ if (selectedVariants.length === 0) {
85
93
  showSnackbar(t('components.product_variant_dialog.select_variant'), { variant: 'error' });
86
94
  return;
87
95
  }
88
- callback(selectedVariant);
89
- setSelectedVariant(null);
96
+
97
+ callback(selectedVariants);
98
+ setDraftQuantities({});
90
99
  onClose?.();
91
100
  };
92
101
 
93
- const isVariantDisabled = (variant: IEventProductVariant) => {
102
+ const getUsedCount = (variant: IEventProductVariant) => {
94
103
  let countUsed = 0;
95
- let selectedVariantQuantity = 0;
96
104
 
97
105
  for (const product of products) {
98
106
  if (product.productVariantId === variant.productVariant.id) {
@@ -105,70 +113,111 @@ const ProductVariantsDialog: React.FC<Props> = ({
105
113
  for (const product of ticket.products) {
106
114
  if (product.productVariantId === variant.productVariant.id) {
107
115
  countUsed += product.quantity;
108
- selectedVariantQuantity = product.quantity;
109
116
  }
110
117
  }
111
118
  }
112
119
  }
120
+ return countUsed;
121
+ };
113
122
 
114
- return (
115
- (variant.productVariant.quantity &&
116
- countUsed + variant.productVariant.sold >= variant.productVariant.quantity) ||
117
- selectedVariantQuantity >= 10
118
- );
123
+ const getMaxAvailable = (variant: IEventProductVariant) => {
124
+ if (!variant.productVariant.quantity) return Number.POSITIVE_INFINITY;
125
+ return variant.productVariant.quantity - variant.productVariant.sold - getUsedCount(variant);
119
126
  };
120
127
 
121
- const isVariantSoldOut = () => {
122
- const variant = eventProduct.eventProductVariants.find(
123
- (product) => product.id === selectedVariant?.eventProductVariantId
124
- );
125
- let countUsed = variantQuantity;
128
+ const isVariantDisabled = (variant: IEventProductVariant) => {
129
+ if (!variant.productVariant.quantity) return false;
130
+ return getMaxAvailable(variant) <= 0;
131
+ };
126
132
 
127
- for (const ticket of tickets) {
128
- if (ticket.products) {
129
- for (const product of ticket.products) {
130
- if (product.productVariantId === variant?.productVariant.id) {
131
- countUsed += product.quantity;
132
- }
133
- }
134
- }
135
- }
133
+ const handleAddQuantity = (variant: IEventProductVariant) => {
134
+ setDraftQuantities((prev) => {
135
+ const current = prev[variant.id] ?? 0;
136
+ const next = current + 1;
137
+ const maxAvailable = getMaxAvailable(variant);
136
138
 
137
- if (variant && variant.productVariant.quantity) {
138
- if (
139
- variant.productVariant.sold + countUsed >= variant.productVariant.quantity ||
140
- variantQuantity >= 10
141
- ) {
142
- return true;
139
+ if (canAddOnlyOneAtATime) {
140
+ if (maxAvailable < 1) return prev;
141
+ return { [variant.id]: 1 };
143
142
  }
144
- }
145
- return false;
143
+
144
+ const maxSelectable =
145
+ maxSelectableQuantity === undefined
146
+ ? MAX_TICKETS_PER_ORDER
147
+ : Math.max(0, Math.min(maxSelectableQuantity, MAX_TICKETS_PER_ORDER));
148
+
149
+ if (next > maxSelectable || next > maxAvailable) return prev;
150
+ return { ...prev, [variant.id]: next };
151
+ });
146
152
  };
147
153
 
148
- const handleAddQuantity = () => {
149
- setVariantQuantity(variantQuantity + 1);
150
- setSelectedVariant((prevState) => {
151
- if (!prevState) return null;
152
- return {
153
- ...prevState,
154
- quantity: variantQuantity + 1,
155
- };
154
+ const handleRemoveQuantity = (variant: IEventProductVariant) => {
155
+ setDraftQuantities((prev) => {
156
+ const current = prev[variant.id] ?? 0;
157
+ const next = current - 1;
158
+ if (next <= 0) {
159
+ const rest = { ...prev };
160
+ delete rest[variant.id];
161
+ return rest;
162
+ }
163
+ if (canAddOnlyOneAtATime) {
164
+ // Remove the only variant
165
+ return {};
166
+ }
167
+ return { ...prev, [variant.id]: next };
156
168
  });
157
169
  };
158
170
 
159
- const handleRemoveQuantity = () => {
160
- if (variantQuantity > 0) {
161
- setVariantQuantity(variantQuantity - 1);
171
+ const handleRemoveProduct = () => {
172
+ const variantsToRemove = eventProduct.eventProductVariants
173
+ .filter((variant) => (selectedQuantityByVariant?.[variant.id] ?? 0) > 0)
174
+ .map(
175
+ (variant) =>
176
+ ({
177
+ eventProductVariantId: variant.id,
178
+ productVariantId: variant.productVariant.id,
179
+ quantity: 0,
180
+ price: variant.priceWithVat,
181
+ excludedShippingMethodIds: eventProduct.excludedShippingMethods.map(
182
+ (method) => method.id
183
+ ),
184
+ }) as IEventProductForm
185
+ );
186
+
187
+ if (variantsToRemove.length === 0) {
188
+ return;
162
189
  }
163
- setSelectedVariant((prevState) => {
164
- if (!prevState) return null;
165
- return {
166
- ...prevState,
167
- quantity: variantQuantity - 1,
168
- };
169
- });
190
+
191
+ callback(variantsToRemove);
192
+ setDraftQuantities({});
193
+ onClose?.();
170
194
  };
171
195
 
196
+ const hasSelectedVariants = eventProduct.eventProductVariants.some(
197
+ (variant) => (selectedQuantityByVariant?.[variant.id] ?? 0) > 0
198
+ );
199
+
200
+ const variantsById = useMemo(
201
+ () => new Map(eventProduct.eventProductVariants.map((variant) => [variant.id, variant])),
202
+ [eventProduct.eventProductVariants]
203
+ );
204
+
205
+ const totalDraftPrice = useMemo(
206
+ () =>
207
+ Object.entries(draftQuantities).reduce((total, [variantId, qty]) => {
208
+ const variant = variantsById.get(Number(variantId));
209
+ if (!variant) return total;
210
+ return total + variant.priceWithVat * qty;
211
+ }, 0),
212
+ [draftQuantities, variantsById]
213
+ );
214
+
215
+ const hasDraftSelection = useMemo(
216
+ () =>
217
+ eventProduct.eventProductVariants.some((variant) => (draftQuantities[variant.id] ?? 0) > 0),
218
+ [eventProduct.eventProductVariants, draftQuantities]
219
+ );
220
+
172
221
  return (
173
222
  <Dialog
174
223
  open={openDialog}
@@ -187,16 +236,19 @@ const ProductVariantsDialog: React.FC<Props> = ({
187
236
  },
188
237
  }}
189
238
  >
190
- <DialogTitle />
191
- <DialogContent>
239
+ <DialogTitle
240
+ sx={{ textAlign: 'center', fontWeight: 700, fontSize: { xs: '1.5rem', sm: '1.5rem' } }}
241
+ >
242
+ {eventProduct.product.name}
243
+ </DialogTitle>
244
+ <DialogContent sx={{ pb: 0 }}>
192
245
  <Stack spacing={1}>
193
246
  <Image
194
247
  src={eventProduct.product.previewImage.url}
195
248
  alt={eventProduct.product.name}
196
- ratio="1/1"
197
- sx={{ borderRadius: 2 }}
249
+ ratio="16/9"
250
+ sx={{ borderRadius: 1 }}
198
251
  />
199
- <Typography variant="h5">{eventProduct.product.name}</Typography>
200
252
  <Typography variant="body2" color="primary">
201
253
  {t('form.labels.category')}: {eventProduct.product.category.value.text}
202
254
  </Typography>
@@ -207,64 +259,165 @@ const ProductVariantsDialog: React.FC<Props> = ({
207
259
  </Stack>
208
260
  </DialogContent>
209
261
  <DialogActions sx={{ justifyContent: 'flex-start' }}>
210
- <Stack spacing={2} width="100%">
211
- <Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap textAlign="left">
212
- {eventProduct.eventProductVariants.map((variant) => (
213
- <Button
214
- key={variant.id}
215
- onClick={() => handleOnClick(variant)}
216
- variant={
217
- variant.id === selectedVariant?.eventProductVariantId ? 'contained' : 'outlined'
218
- }
219
- size="large"
220
- disabled={isOnlyMerchandise ? false : isVariantDisabled(variant)}
221
- >
222
- <Badge
223
- color="primary"
224
- badgeContent={
225
- selectedQuantityByVariant ? selectedQuantityByVariant[variant.id] : 0
226
- }
227
- sx={{ position: 'unset' }}
262
+ <Stack spacing={1} width="100%">
263
+ <Stack spacing={1}>
264
+ {eventProduct.eventProductVariants.map((variant) => {
265
+ const draftQty = draftQuantities[variant.id] ?? 0;
266
+ const maxAvailable = getMaxAvailable(variant);
267
+ const maxSelectable =
268
+ maxSelectableQuantity === undefined
269
+ ? MAX_TICKETS_PER_ORDER
270
+ : Math.max(0, Math.min(maxSelectableQuantity, MAX_TICKETS_PER_ORDER));
271
+ const isAddDisabled =
272
+ isVariantDisabled(variant) ||
273
+ maxAvailable <= 0 ||
274
+ (!canAddOnlyOneAtATime && maxSelectable <= 0);
275
+ const isAnotherSelected =
276
+ canAddOnlyOneAtATime &&
277
+ Object.keys(draftQuantities).length > 0 &&
278
+ !draftQuantities[variant.id];
279
+ return (
280
+ <Stack
281
+ key={variant.id}
282
+ direction="row"
283
+ spacing={1}
284
+ alignItems="center"
285
+ width="100%"
228
286
  >
229
- {variant.productVariant.value}
230
- </Badge>
231
- </Button>
232
- ))}
287
+ <Box
288
+ sx={{
289
+ minWidth: 64,
290
+ maxWidth: 64,
291
+ height: 36,
292
+ borderRadius: 1,
293
+ border: 1,
294
+ borderColor: 'divider',
295
+ bgcolor: 'background.paper',
296
+ display: 'flex',
297
+ alignItems: 'center',
298
+ justifyContent: 'center',
299
+ }}
300
+ >
301
+ <Typography variant="body2" fontWeight={600}>
302
+ {variant.productVariant.value}
303
+ </Typography>
304
+ </Box>
305
+ {draftQty > 0 ? (
306
+ <Box
307
+ sx={{
308
+ flex: 1,
309
+ display: 'grid',
310
+ gridTemplateColumns: canAddOnlyOneAtATime
311
+ ? 'repeat(2, minmax(0, 1fr))'
312
+ : 'repeat(3, minmax(0, 1fr))',
313
+ gap: 1,
314
+ alignItems: 'center',
315
+ }}
316
+ >
317
+ <Button
318
+ variant="outlined"
319
+ onClick={() => handleRemoveQuantity(variant)}
320
+ aria-label={t('components.product_variant_dialog.decrease_quantity')}
321
+ disabled={draftQty < 1}
322
+ sx={{
323
+ minWidth: 0,
324
+ height: 36,
325
+ borderRadius: 1,
326
+ fontWeight: 700,
327
+ borderColor: 'grey.300',
328
+ color: 'text.primary',
329
+ }}
330
+ >
331
+ -
332
+ </Button>
333
+ <Typography
334
+ sx={{
335
+ minWidth: 0,
336
+ height: 36,
337
+ borderRadius: 1,
338
+ border: 1,
339
+ borderColor: 'divider',
340
+ boxSizing: 'border-box',
341
+ display: 'flex',
342
+ alignItems: 'center',
343
+ justifyContent: 'center',
344
+ textAlign: 'center',
345
+ fontWeight: 700,
346
+ color: 'text.primary',
347
+ }}
348
+ >
349
+ {draftQty}
350
+ </Typography>
351
+ {!canAddOnlyOneAtATime && (
352
+ <Button
353
+ variant="contained"
354
+ onClick={() => handleAddQuantity(variant)}
355
+ aria-label={t('components.product_variant_dialog.increase_quantity')}
356
+ disabled={draftQty >= maxSelectable || draftQty >= maxAvailable}
357
+ sx={{ minWidth: 0, height: 36, borderRadius: 1, fontWeight: 700 }}
358
+ >
359
+ +
360
+ </Button>
361
+ )}
362
+ </Box>
363
+ ) : (
364
+ <Button
365
+ variant="contained"
366
+ onClick={() => handleAddQuantity(variant)}
367
+ disabled={isOnlyMerchandise ? false : isAddDisabled || isAnotherSelected}
368
+ sx={{
369
+ flex: 1,
370
+ height: 36,
371
+ borderRadius: 1,
372
+ fontWeight: 700,
373
+ textTransform: 'none',
374
+ }}
375
+ >
376
+ {t('add')}
377
+ </Button>
378
+ )}
379
+
380
+ <Typography variant="body2">
381
+ {variant.priceWithVat > 0
382
+ ? fCurrency(variant.priceWithVat, lang, eventProduct.product.currency)
383
+ : t('free')}
384
+ </Typography>
385
+ </Stack>
386
+ );
387
+ })}
233
388
  </Stack>
234
- <Stack direction="row" justifyContent="space-between" alignItems="center" pt={1}>
235
- {selectedVariant && isOnlyMerchandise && (
236
- <Stack direction="row" alignItems="center" spacing={1}>
237
- <IconButton
238
- size="small"
239
- color="primary"
240
- onClick={handleRemoveQuantity}
241
- disabled={variantQuantity < 1}
242
- >
243
- <Iconify icon="eva:minus-fill" />
244
- </IconButton>
245
- <Typography color="grey.500">{variantQuantity}</Typography>
246
- <IconButton
247
- size="small"
248
- color="primary"
249
- onClick={handleAddQuantity}
250
- disabled={isVariantSoldOut()}
251
- >
252
- <Iconify icon="eva:plus-fill" />
253
- </IconButton>
254
- </Stack>
255
- )}
256
- <Typography variant="h4" textAlign="right" mt={0}>
257
- {variantPrice !== null
258
- ? variantPrice > 0
259
- ? fCurrency(variantPrice, lang, eventProduct.product.currency)
260
- : t('free')
261
- : t('unselected')}
389
+ <Stack
390
+ direction="row"
391
+ spacing={1}
392
+ justifyContent="space-between"
393
+ alignItems="center"
394
+ width="100%"
395
+ >
396
+ <Typography variant="body2">{t('form.labels.total')}</Typography>
397
+ <Typography variant="body2" fontWeight={600}>
398
+ {fCurrency(totalDraftPrice, lang, eventProduct.product.currency)}
262
399
  </Typography>
263
400
  </Stack>
264
401
  <Stack direction="row" spacing={1} justifyContent="flex-end" width="100%">
265
- {onClose && <Button onClick={onClose}>{t('cancel')}</Button>}
266
- <Button variant="contained" onClick={handleOnAdd} disabled={!selectedVariant}>
267
- {t('add')}
402
+ {hasSelectedVariants && !canAddOnlyOneAtATime && (
403
+ <Button variant="outlined" color="error" onClick={handleRemoveProduct}>
404
+ {t('remove')}
405
+ </Button>
406
+ )}
407
+ {onClose && (
408
+ <Button
409
+ onClick={onClose}
410
+ sx={{
411
+ border: 1,
412
+ color: 'inherit',
413
+ borderColor: (theme) => theme.palette.grey[300],
414
+ }}
415
+ >
416
+ {t('cancel')}
417
+ </Button>
418
+ )}
419
+ <Button variant="contained" onClick={handleOnAdd} disabled={!hasDraftSelection}>
420
+ {t('confirm')}
268
421
  </Button>
269
422
  </Stack>
270
423
  </Stack>