@eventlook/sdk 1.5.0-beta.5 → 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 (92) 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-DLC5Hr8L.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-BSCKGDNQ.js → index.umd-Bpwd9vUs.js} +9 -19
  8. package/dist/cjs/index.umd-Bpwd9vUs.js.map +1 -0
  9. package/dist/esm/{index-BwmbJihM.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-6fgyuQlr.js → index.umd-ewNTELOK.js} +9 -19
  13. package/dist/esm/index.umd-ewNTELOK.js.map +1 -0
  14. package/dist/types/components/animate/MotionContainer.d.ts +1 -1
  15. package/dist/types/components/animate/MotionViewport.d.ts +1 -1
  16. package/dist/types/components/animate/TextAnimate.d.ts +1 -1
  17. package/dist/types/components/hook-form/FormProvider.d.ts +2 -1
  18. package/dist/types/form/PaymentOverviewBox.d.ts +2 -0
  19. package/dist/types/form/PaymentOverviewDrawer.d.ts +10 -0
  20. package/dist/types/form/TicketForm.d.ts +1 -0
  21. package/dist/types/form/index.d.ts +2 -1
  22. package/dist/types/form/merchandise/MerchandiseSelection.d.ts +9 -0
  23. package/dist/types/form/merchandise/MerchandiseSlider.d.ts +10 -0
  24. package/dist/types/form/payment/PaymentOverviewCheckbox.d.ts +0 -4
  25. package/dist/types/form/product/ProductVariantsDialog.d.ts +2 -1
  26. package/dist/types/form/services/index.d.ts +7 -0
  27. package/dist/types/form/style.d.ts +1 -0
  28. package/dist/types/form/tickets/ReleaseDescription.d.ts +10 -0
  29. package/dist/types/form/tickets/ReleaseWithMerchandise.d.ts +12 -0
  30. package/dist/types/form/tickets/TicketQuantityControl.d.ts +13 -0
  31. package/dist/types/form/tickets/TicketSelectionMobile.d.ts +17 -0
  32. package/dist/types/hooks/useScrollToFirstError.d.ts +4 -0
  33. package/dist/types/locales/cs.d.ts +22 -0
  34. package/dist/types/locales/en.d.ts +22 -0
  35. package/dist/types/locales/es.d.ts +22 -0
  36. package/dist/types/locales/pl.d.ts +22 -0
  37. package/dist/types/locales/sk.d.ts +22 -0
  38. package/dist/types/locales/uk.d.ts +22 -0
  39. package/dist/types/utils/data/global.d.ts +1 -0
  40. package/package.json +11 -5
  41. package/rollup.config.mjs +7 -12
  42. package/src/components/animate/DialogAnimate.tsx +1 -1
  43. package/src/components/animate/IconButtonAnimate.tsx +1 -1
  44. package/src/components/animate/MotionContainer.tsx +1 -1
  45. package/src/components/animate/MotionLazyContainer.tsx +1 -1
  46. package/src/components/animate/MotionViewport.tsx +1 -1
  47. package/src/components/animate/TextAnimate.tsx +1 -1
  48. package/src/components/animate/features.js +1 -1
  49. package/src/components/hook-form/FormProvider.tsx +5 -2
  50. package/src/form/ChildEventDialog.tsx +3 -3
  51. package/src/form/ContactPerson.tsx +1 -1
  52. package/src/form/OrderSuccess.tsx +1 -1
  53. package/src/form/PaymentOverviewBox.tsx +96 -123
  54. package/src/form/PaymentOverviewDrawer.tsx +446 -0
  55. package/src/form/PaymentPending.tsx +20 -5
  56. package/src/form/PaymentSuccess.tsx +1 -1
  57. package/src/form/ReleaseWithMerchandise.tsx +4 -4
  58. package/src/form/Shipping.tsx +91 -74
  59. package/src/form/TicketForm.tsx +144 -41
  60. package/src/form/index.tsx +3 -1
  61. package/src/form/merchandise/MerchandiseSelection.tsx +24 -0
  62. package/src/form/merchandise/MerchandiseSlider.tsx +62 -0
  63. package/src/form/payment/FeeBox.tsx +4 -31
  64. package/src/form/payment/PaymentOverviewCheckbox.tsx +57 -56
  65. package/src/form/product/ProductCard.tsx +255 -59
  66. package/src/form/product/ProductVariantsDialog.tsx +271 -141
  67. package/src/form/services/index.tsx +263 -0
  68. package/src/form/style.ts +16 -4
  69. package/src/form/tickets/ReleaseDescription.tsx +46 -0
  70. package/src/form/tickets/ReleaseWithMerchandise.tsx +231 -0
  71. package/src/form/tickets/TicketQuantityControl.tsx +100 -0
  72. package/src/form/{TicketSelection.tsx → tickets/TicketSelection.tsx} +24 -128
  73. package/src/form/{TicketSelectionMap.tsx → tickets/TicketSelectionMap.tsx} +9 -1
  74. package/src/form/tickets/TicketSelectionMobile.tsx +177 -0
  75. package/src/form/{TicketWithMerchandiseSelection.tsx → tickets/TicketWithMerchandiseSelection.tsx} +3 -7
  76. package/src/hooks/useScrollToFirstError.ts +99 -0
  77. package/src/locales/cs.tsx +25 -3
  78. package/src/locales/en.tsx +23 -1
  79. package/src/locales/es.tsx +23 -1
  80. package/src/locales/pl.tsx +23 -1
  81. package/src/locales/sk.tsx +24 -2
  82. package/src/locales/uk.tsx +23 -1
  83. package/src/utils/data/global.ts +1 -0
  84. package/tsconfig.json +1 -1
  85. package/README +0 -1
  86. package/dist/cjs/index-DLC5Hr8L.js.map +0 -1
  87. package/dist/cjs/index.umd-BSCKGDNQ.js.map +0 -1
  88. package/dist/esm/index-BwmbJihM.js.map +0 -1
  89. package/dist/esm/index.umd-6fgyuQlr.js.map +0 -1
  90. /package/dist/types/form/{TicketSelection.d.ts → tickets/TicketSelection.d.ts} +0 -0
  91. /package/dist/types/form/{TicketSelectionMap.d.ts → tickets/TicketSelectionMap.d.ts} +0 -0
  92. /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';
@@ -17,16 +16,16 @@ import { ITicketFormTicket } from '@utils/types/ticket.type';
17
16
  import { IEventProductForm } from '@utils/types/product.type';
18
17
  import { fCurrency } from '@utils/formatNumber';
19
18
  import useGlobal from '@hooks/useGlobal';
20
- import { Iconify } from '@components';
21
19
 
22
20
  interface Props {
23
21
  eventProduct: IEventProduct;
24
22
  eventId: number;
25
23
  openDialog: boolean;
26
- callback: (variant: IEventProductForm) => void;
24
+ callback: (variants: IEventProductForm[]) => void;
27
25
  onClose?: () => void;
28
26
  selectedQuantityByVariant?: Record<number, number>;
29
27
  isOnlyMerchandise?: boolean;
28
+ canAddOnlyOneAtATime?: boolean;
30
29
  }
31
30
 
32
31
  const ProductVariantsDialog: React.FC<Props> = ({
@@ -37,13 +36,11 @@ const ProductVariantsDialog: React.FC<Props> = ({
37
36
  selectedQuantityByVariant,
38
37
  isOnlyMerchandise,
39
38
  eventId,
39
+ canAddOnlyOneAtATime,
40
40
  }) => {
41
41
  const { t, lang, options } = useGlobal();
42
42
  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);
43
+ const [draftQuantities, setDraftQuantities] = useState<Record<number, number>>({});
47
44
  const tickets: ITicketFormTicket[] = useWatch({ name: `tickets.${eventId}`, defaultValue: [] });
48
45
  const products: IEventProductForm[] = useWatch({ name: `products.${eventId}`, defaultValue: [] });
49
46
 
@@ -56,43 +53,45 @@ const ProductVariantsDialog: React.FC<Props> = ({
56
53
  }, [openDialog]);
57
54
 
58
55
  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);
56
+ if (openDialog) {
57
+ setDraftQuantities(canAddOnlyOneAtATime ? {} : (selectedQuantityByVariant ?? {}));
68
58
  }
69
- }, [selectedVariant, isVariantClicked]);
70
-
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
- };
59
+ }, [openDialog, selectedQuantityByVariant, canAddOnlyOneAtATime]);
82
60
 
83
61
  const handleOnAdd = () => {
84
- if (!selectedVariant) {
62
+ const selectedVariants = eventProduct.eventProductVariants
63
+ .map((variant) => {
64
+ const quantity = draftQuantities[variant.id] ?? 0;
65
+ const selectedQty = selectedQuantityByVariant?.[variant.id] ?? 0;
66
+ if (canAddOnlyOneAtATime) {
67
+ if (quantity <= 0) return null;
68
+ } else if (quantity <= 0 && selectedQty <= 0) {
69
+ return null;
70
+ }
71
+ return {
72
+ eventProductVariantId: variant.id,
73
+ productVariantId: variant.productVariant.id,
74
+ quantity,
75
+ price: variant.priceWithVat,
76
+ excludedShippingMethodIds: eventProduct.excludedShippingMethods.map(
77
+ (method) => method.id
78
+ ),
79
+ } as IEventProductForm;
80
+ })
81
+ .filter((variant): variant is IEventProductForm => Boolean(variant));
82
+
83
+ if (selectedVariants.length === 0) {
85
84
  showSnackbar(t('components.product_variant_dialog.select_variant'), { variant: 'error' });
86
85
  return;
87
86
  }
88
- callback(selectedVariant);
89
- setSelectedVariant(null);
87
+
88
+ callback(selectedVariants);
89
+ setDraftQuantities({});
90
90
  onClose?.();
91
91
  };
92
92
 
93
- const isVariantDisabled = (variant: IEventProductVariant) => {
93
+ const getUsedCount = (variant: IEventProductVariant) => {
94
94
  let countUsed = 0;
95
- let selectedVariantQuantity = 0;
96
95
 
97
96
  for (const product of products) {
98
97
  if (product.productVariantId === variant.productVariant.id) {
@@ -105,70 +104,104 @@ const ProductVariantsDialog: React.FC<Props> = ({
105
104
  for (const product of ticket.products) {
106
105
  if (product.productVariantId === variant.productVariant.id) {
107
106
  countUsed += product.quantity;
108
- selectedVariantQuantity = product.quantity;
109
107
  }
110
108
  }
111
109
  }
112
110
  }
113
-
114
- return (
115
- (variant.productVariant.quantity &&
116
- countUsed + variant.productVariant.sold >= variant.productVariant.quantity) ||
117
- selectedVariantQuantity >= 10
118
- );
111
+ return countUsed;
119
112
  };
120
113
 
121
- const isVariantSoldOut = () => {
122
- const variant = eventProduct.eventProductVariants.find(
123
- (product) => product.id === selectedVariant?.eventProductVariantId
124
- );
125
- let countUsed = variantQuantity;
114
+ const getMaxAvailable = (variant: IEventProductVariant) => {
115
+ if (!variant.productVariant.quantity) return Number.POSITIVE_INFINITY;
116
+ return variant.productVariant.quantity - variant.productVariant.sold - getUsedCount(variant);
117
+ };
126
118
 
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
- }
119
+ const isVariantDisabled = (variant: IEventProductVariant) => {
120
+ if (!variant.productVariant.quantity) return false;
121
+ return getMaxAvailable(variant) <= 0;
122
+ };
136
123
 
137
- if (variant && variant.productVariant.quantity) {
138
- if (
139
- variant.productVariant.sold + countUsed >= variant.productVariant.quantity ||
140
- variantQuantity >= 10
141
- ) {
142
- return true;
124
+ const handleAddQuantity = (variant: IEventProductVariant) => {
125
+ setDraftQuantities((prev) => {
126
+ const current = prev[variant.id] ?? 0;
127
+ const next = current + 1;
128
+ const maxAvailable = getMaxAvailable(variant);
129
+ if (next > 10 || next > maxAvailable) return prev;
130
+ if (canAddOnlyOneAtATime) {
131
+ // Only allow one variant to be selected
132
+ return { [variant.id]: 1 };
143
133
  }
144
- }
145
- return false;
134
+ return { ...prev, [variant.id]: next };
135
+ });
146
136
  };
147
137
 
148
- const handleAddQuantity = () => {
149
- setVariantQuantity(variantQuantity + 1);
150
- setSelectedVariant((prevState) => {
151
- if (!prevState) return null;
152
- return {
153
- ...prevState,
154
- quantity: variantQuantity + 1,
155
- };
138
+ const handleRemoveQuantity = (variant: IEventProductVariant) => {
139
+ setDraftQuantities((prev) => {
140
+ const current = prev[variant.id] ?? 0;
141
+ const next = current - 1;
142
+ if (next <= 0) {
143
+ const rest = { ...prev };
144
+ delete rest[variant.id];
145
+ return rest;
146
+ }
147
+ if (canAddOnlyOneAtATime) {
148
+ // Remove the only variant
149
+ return {};
150
+ }
151
+ return { ...prev, [variant.id]: next };
156
152
  });
157
153
  };
158
154
 
159
- const handleRemoveQuantity = () => {
160
- if (variantQuantity > 0) {
161
- setVariantQuantity(variantQuantity - 1);
155
+ const handleRemoveProduct = () => {
156
+ const variantsToRemove = eventProduct.eventProductVariants
157
+ .filter((variant) => (selectedQuantityByVariant?.[variant.id] ?? 0) > 0)
158
+ .map(
159
+ (variant) =>
160
+ ({
161
+ eventProductVariantId: variant.id,
162
+ productVariantId: variant.productVariant.id,
163
+ quantity: 0,
164
+ price: variant.priceWithVat,
165
+ excludedShippingMethodIds: eventProduct.excludedShippingMethods.map(
166
+ (method) => method.id
167
+ ),
168
+ }) as IEventProductForm
169
+ );
170
+
171
+ if (variantsToRemove.length === 0) {
172
+ return;
162
173
  }
163
- setSelectedVariant((prevState) => {
164
- if (!prevState) return null;
165
- return {
166
- ...prevState,
167
- quantity: variantQuantity - 1,
168
- };
169
- });
174
+
175
+ callback(variantsToRemove);
176
+ setDraftQuantities({});
177
+ onClose?.();
170
178
  };
171
179
 
180
+ const hasSelectedVariants = eventProduct.eventProductVariants.some(
181
+ (variant) => (selectedQuantityByVariant?.[variant.id] ?? 0) > 0
182
+ );
183
+
184
+ const variantsById = useMemo(
185
+ () => new Map(eventProduct.eventProductVariants.map((variant) => [variant.id, variant])),
186
+ [eventProduct.eventProductVariants]
187
+ );
188
+
189
+ const totalDraftPrice = useMemo(
190
+ () =>
191
+ Object.entries(draftQuantities).reduce((total, [variantId, qty]) => {
192
+ const variant = variantsById.get(Number(variantId));
193
+ if (!variant) return total;
194
+ return total + variant.priceWithVat * qty;
195
+ }, 0),
196
+ [draftQuantities, variantsById]
197
+ );
198
+
199
+ const hasDraftSelection = useMemo(
200
+ () =>
201
+ eventProduct.eventProductVariants.some((variant) => (draftQuantities[variant.id] ?? 0) > 0),
202
+ [eventProduct.eventProductVariants, draftQuantities]
203
+ );
204
+
172
205
  return (
173
206
  <Dialog
174
207
  open={openDialog}
@@ -187,16 +220,19 @@ const ProductVariantsDialog: React.FC<Props> = ({
187
220
  },
188
221
  }}
189
222
  >
190
- <DialogTitle />
191
- <DialogContent>
223
+ <DialogTitle
224
+ sx={{ textAlign: 'center', fontWeight: 700, fontSize: { xs: '1.5rem', sm: '1.5rem' } }}
225
+ >
226
+ {eventProduct.product.name}
227
+ </DialogTitle>
228
+ <DialogContent sx={{ pb: 0 }}>
192
229
  <Stack spacing={1}>
193
230
  <Image
194
231
  src={eventProduct.product.previewImage.url}
195
232
  alt={eventProduct.product.name}
196
- ratio="1/1"
197
- sx={{ borderRadius: 2 }}
233
+ ratio="16/9"
234
+ sx={{ borderRadius: 1 }}
198
235
  />
199
- <Typography variant="h5">{eventProduct.product.name}</Typography>
200
236
  <Typography variant="body2" color="primary">
201
237
  {t('form.labels.category')}: {eventProduct.product.category.value.text}
202
238
  </Typography>
@@ -207,64 +243,158 @@ const ProductVariantsDialog: React.FC<Props> = ({
207
243
  </Stack>
208
244
  </DialogContent>
209
245
  <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' }}
246
+ <Stack spacing={1} width="100%">
247
+ <Stack spacing={1}>
248
+ {eventProduct.eventProductVariants.map((variant) => {
249
+ const draftQty = draftQuantities[variant.id] ?? 0;
250
+ const maxAvailable = getMaxAvailable(variant);
251
+ const isAddDisabled = isVariantDisabled(variant) || maxAvailable <= 0;
252
+ const isAnotherSelected =
253
+ canAddOnlyOneAtATime &&
254
+ Object.keys(draftQuantities).length > 0 &&
255
+ !draftQuantities[variant.id];
256
+ return (
257
+ <Stack
258
+ key={variant.id}
259
+ direction="row"
260
+ spacing={1}
261
+ alignItems="center"
262
+ width="100%"
228
263
  >
229
- {variant.productVariant.value}
230
- </Badge>
231
- </Button>
232
- ))}
264
+ <Box
265
+ sx={{
266
+ minWidth: 64,
267
+ maxWidth: 64,
268
+ height: 36,
269
+ borderRadius: 1,
270
+ border: 1,
271
+ borderColor: 'divider',
272
+ bgcolor: 'background.paper',
273
+ display: 'flex',
274
+ alignItems: 'center',
275
+ justifyContent: 'center',
276
+ }}
277
+ >
278
+ <Typography variant="body2" fontWeight={600}>
279
+ {variant.productVariant.value}
280
+ </Typography>
281
+ </Box>
282
+ {draftQty > 0 ? (
283
+ <Box
284
+ sx={{
285
+ flex: 1,
286
+ display: 'grid',
287
+ gridTemplateColumns: canAddOnlyOneAtATime
288
+ ? 'repeat(2, minmax(0, 1fr))'
289
+ : 'repeat(3, minmax(0, 1fr))',
290
+ gap: 1,
291
+ alignItems: 'center',
292
+ }}
293
+ >
294
+ <Button
295
+ variant="outlined"
296
+ onClick={() => handleRemoveQuantity(variant)}
297
+ aria-label={t('components.product_variant_dialog.decrease_quantity')}
298
+ disabled={draftQty < 1}
299
+ sx={{
300
+ minWidth: 0,
301
+ height: 36,
302
+ borderRadius: 1,
303
+ fontWeight: 700,
304
+ borderColor: 'grey.300',
305
+ color: 'text.primary',
306
+ }}
307
+ >
308
+ -
309
+ </Button>
310
+ <Typography
311
+ sx={{
312
+ minWidth: 0,
313
+ height: 36,
314
+ borderRadius: 1,
315
+ border: 1,
316
+ borderColor: 'divider',
317
+ boxSizing: 'border-box',
318
+ display: 'flex',
319
+ alignItems: 'center',
320
+ justifyContent: 'center',
321
+ textAlign: 'center',
322
+ fontWeight: 700,
323
+ color: 'text.primary',
324
+ }}
325
+ >
326
+ {draftQty}
327
+ </Typography>
328
+ {!canAddOnlyOneAtATime && (
329
+ <Button
330
+ variant="contained"
331
+ onClick={() => handleAddQuantity(variant)}
332
+ aria-label={t('components.product_variant_dialog.increase_quantity')}
333
+ disabled={draftQty >= 10 || draftQty >= maxAvailable}
334
+ sx={{ minWidth: 0, height: 36, borderRadius: 1, fontWeight: 700 }}
335
+ >
336
+ +
337
+ </Button>
338
+ )}
339
+ </Box>
340
+ ) : (
341
+ <Button
342
+ variant="contained"
343
+ onClick={() => handleAddQuantity(variant)}
344
+ disabled={isOnlyMerchandise ? false : isAddDisabled || isAnotherSelected}
345
+ sx={{
346
+ flex: 1,
347
+ height: 36,
348
+ borderRadius: 1,
349
+ fontWeight: 700,
350
+ textTransform: 'none',
351
+ }}
352
+ >
353
+ {t('add')}
354
+ </Button>
355
+ )}
356
+
357
+ <Typography variant="body2">
358
+ {variant.priceWithVat > 0
359
+ ? fCurrency(variant.priceWithVat, lang, eventProduct.product.currency)
360
+ : t('free')}
361
+ </Typography>
362
+ </Stack>
363
+ );
364
+ })}
233
365
  </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')}
366
+ <Stack
367
+ direction="row"
368
+ spacing={1}
369
+ justifyContent="space-between"
370
+ alignItems="center"
371
+ width="100%"
372
+ >
373
+ <Typography variant="body2">{t('form.labels.total')}</Typography>
374
+ <Typography variant="body2" fontWeight={600}>
375
+ {fCurrency(totalDraftPrice, lang, eventProduct.product.currency)}
262
376
  </Typography>
263
377
  </Stack>
264
378
  <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')}
379
+ {hasSelectedVariants && !canAddOnlyOneAtATime && (
380
+ <Button variant="outlined" color="error" onClick={handleRemoveProduct}>
381
+ {t('remove')}
382
+ </Button>
383
+ )}
384
+ {onClose && (
385
+ <Button
386
+ onClick={onClose}
387
+ sx={{
388
+ border: 1,
389
+ color: 'inherit',
390
+ borderColor: (theme) => theme.palette.grey[300],
391
+ }}
392
+ >
393
+ {t('cancel')}
394
+ </Button>
395
+ )}
396
+ <Button variant="contained" onClick={handleOnAdd} disabled={!hasDraftSelection}>
397
+ {t('confirm')}
268
398
  </Button>
269
399
  </Stack>
270
400
  </Stack>