@licklist/design 0.71.18-dev.3 → 0.71.18-dev.5

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 (101) hide show
  1. package/dist/events/edit-event-modal/component/SelectEventProductSet/component/EditEventProductSet.js +1 -0
  2. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetForm.d.ts +18 -0
  3. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetForm.d.ts.map +1 -0
  4. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetForm.js +281 -0
  5. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetModal.d.ts +15 -0
  6. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetModal.d.ts.map +1 -0
  7. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetModal.js +89 -0
  8. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/controll/ModifierSetControll.d.ts +14 -0
  9. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/controll/ModifierSetControll.d.ts.map +1 -0
  10. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/controll/ModifierSetControll.js +399 -0
  11. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/controll/ProductControll.d.ts +10 -0
  12. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/controll/ProductControll.d.ts.map +1 -0
  13. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/controll/ProductControll.js +87 -0
  14. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/index.d.ts +2 -0
  15. package/dist/iframe/ProductWithModifierModal/ModifierSetModal/index.d.ts.map +1 -0
  16. package/dist/iframe/ProductWithModifierModal/index.d.ts +2 -0
  17. package/dist/iframe/ProductWithModifierModal/index.d.ts.map +1 -0
  18. package/dist/iframe/ProductWithModifierModal/utils.d.ts +5 -0
  19. package/dist/iframe/ProductWithModifierModal/utils.d.ts.map +1 -0
  20. package/dist/iframe/ProductWithModifierModal/utils.js +19 -0
  21. package/dist/iframe/event/ticket-description/TicketDescription.d.ts +2 -1
  22. package/dist/iframe/event/ticket-description/TicketDescription.d.ts.map +1 -1
  23. package/dist/iframe/event/ticket-description/TicketDescription.js +4 -3
  24. package/dist/iframe/order-process/components/BookingSummary/components/ProductSummary/ProductSummary.d.ts.map +1 -1
  25. package/dist/iframe/order-process/components/BookingSummary/components/ProductSummary/ProductSummary.js +29 -2
  26. package/dist/iframe/order-process/components/BookingSummary/utils/index.d.ts +4 -0
  27. package/dist/iframe/order-process/components/BookingSummary/utils/index.d.ts.map +1 -1
  28. package/dist/iframe/order-process/components/BookingSummary/utils/index.js +27 -2
  29. package/dist/iframe/order-process/components/CategoryProduct/CategoryProduct.d.ts.map +1 -1
  30. package/dist/iframe/order-process/components/CategoryProduct/CategoryProduct.js +106 -2
  31. package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.d.ts +3 -1
  32. package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.d.ts.map +1 -1
  33. package/dist/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.js +95 -1
  34. package/dist/iframe/order-process/components/CategoryProduct/components/ProductWithModifier/ProductWithModifier.d.ts +22 -0
  35. package/dist/iframe/order-process/components/CategoryProduct/components/ProductWithModifier/ProductWithModifier.d.ts.map +1 -0
  36. package/dist/iframe/order-process/components/CategoryProduct/components/ProductWithModifier/ProductWithModifier.js +62 -0
  37. package/dist/iframe/order-process/components/CategoryProduct/constants.d.ts +2 -0
  38. package/dist/iframe/order-process/components/CategoryProduct/constants.d.ts.map +1 -0
  39. package/dist/iframe/order-process/components/CategoryProduct/constants.js +4 -0
  40. package/dist/iframe/order-process/components/utils/useOnWindowUnmount.d.ts +6 -0
  41. package/dist/iframe/order-process/components/utils/useOnWindowUnmount.d.ts.map +1 -0
  42. package/dist/iframe/order-process/components/utils/useOnWindowUnmount.js +18 -0
  43. package/dist/iframe/payment/order-items-table/hooks/useTableData.d.ts.map +1 -1
  44. package/dist/iframe/payment/order-items-table/hooks/useTableData.js +83 -10
  45. package/dist/iframe/payment/order-items-table/utils/index.d.ts.map +1 -1
  46. package/dist/iframe/payment/order-items-table/utils/index.js +15 -0
  47. package/dist/iframe/payment/order-items-table/utils/paymentSummary.js +2 -2
  48. package/dist/product-set/form/ProductSetForm.d.ts +3 -1
  49. package/dist/product-set/form/ProductSetForm.d.ts.map +1 -1
  50. package/dist/product-set/form/ProductSetForm.js +6 -4
  51. package/dist/product-set/form/ProductsControl.d.ts.map +1 -1
  52. package/dist/product-set/form/ProductsControl.js +17 -5
  53. package/dist/product-set/form/context.d.ts +3 -1
  54. package/dist/product-set/form/context.d.ts.map +1 -1
  55. package/dist/product-set/form/context.js +2 -1
  56. package/dist/product-set/product/ProductControl.d.ts +8 -0
  57. package/dist/product-set/product/ProductControl.d.ts.map +1 -1
  58. package/dist/product-set/product/ProductControl.js +25 -1
  59. package/dist/product-set/utils/index.d.ts +88 -0
  60. package/dist/product-set/utils/index.d.ts.map +1 -1
  61. package/dist/product-set/utils/index.js +19 -1
  62. package/dist/sales/coupon/utils/index.d.ts +2 -1
  63. package/dist/sales/coupon/utils/index.d.ts.map +1 -1
  64. package/dist/styles/iframe-page/Page.scss +16 -0
  65. package/dist/styles/iframe-page/PageBody.scss +4 -0
  66. package/dist/styles/modals/Modals.scss +16 -0
  67. package/dist/styles/product-set/ProductSetForm.scss +10 -0
  68. package/dist/styles/sales/ManualBooking.scss +6 -0
  69. package/dist/styles/themes/bookedit/index.scss +19 -0
  70. package/package.json +6 -6
  71. package/src/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetForm.tsx +205 -0
  72. package/src/iframe/ProductWithModifierModal/ModifierSetModal/ProductWithModifierSetModal.tsx +75 -0
  73. package/src/iframe/ProductWithModifierModal/ModifierSetModal/controll/ModifierSetControll.tsx +387 -0
  74. package/src/iframe/ProductWithModifierModal/ModifierSetModal/controll/ProductControll.tsx +96 -0
  75. package/src/iframe/ProductWithModifierModal/ModifierSetModal/index.ts +1 -0
  76. package/src/iframe/ProductWithModifierModal/index.ts +1 -0
  77. package/src/iframe/ProductWithModifierModal/utils.ts +28 -0
  78. package/src/iframe/event/ticket-description/TicketDescription.tsx +5 -3
  79. package/src/iframe/order-process/components/BookingSummary/components/ProductSummary/ProductSummary.tsx +32 -1
  80. package/src/iframe/order-process/components/BookingSummary/utils/index.ts +45 -1
  81. package/src/iframe/order-process/components/CategoryProduct/CategoryProduct.tsx +155 -75
  82. package/src/iframe/order-process/components/CategoryProduct/components/ProductQuantityInput/ProductQuantityInput.tsx +58 -1
  83. package/src/iframe/order-process/components/CategoryProduct/components/ProductWithModifier/ProductWithModifier.tsx +84 -0
  84. package/src/iframe/order-process/components/CategoryProduct/constants.ts +1 -0
  85. package/src/iframe/order-process/components/utils/useOnWindowUnmount.ts +25 -0
  86. package/src/iframe/payment/order-items-table/hooks/useTableData.tsx +84 -14
  87. package/src/iframe/payment/order-items-table/utils/index.ts +23 -0
  88. package/src/iframe/payment/order-items-table/utils/paymentSummary.tsx +2 -2
  89. package/src/product-set/form/ProductSetForm.tsx +11 -3
  90. package/src/product-set/form/ProductsControl.tsx +24 -15
  91. package/src/product-set/form/context.tsx +5 -0
  92. package/src/product-set/product/ProductControl.tsx +37 -1
  93. package/src/product-set/utils/index.ts +19 -0
  94. package/src/sales/coupon/utils/index.ts +5 -3
  95. package/src/styles/iframe-page/Page.scss +16 -0
  96. package/src/styles/iframe-page/PageBody.scss +4 -0
  97. package/src/styles/modals/Modals.scss +16 -0
  98. package/src/styles/product-set/ProductSetForm.scss +10 -0
  99. package/src/styles/sales/ManualBooking.scss +6 -0
  100. package/src/styles/themes/bookedit/index.scss +19 -0
  101. package/yarn.lock +177 -205
@@ -0,0 +1,387 @@
1
+ import { Col, Form } from 'react-bootstrap'
2
+ import { useTranslation } from 'react-i18next'
3
+ import { OrderModifier } from '@licklist/core/dist/DataMapper/Order/OrderModifierDataMapper'
4
+ import { Modifier } from '@licklist/core/dist/DataMapper/Product/ModifierDataMapper'
5
+ import { ModifierSet } from '@licklist/core/dist/DataMapper/Product/ModifierSetDataMapper'
6
+ import { ChangeEvent, useEffect, useMemo } from 'react'
7
+ import { useId } from '@mantine/hooks'
8
+ import { Controller, useFormContext } from 'react-hook-form'
9
+ import { xor } from 'lodash'
10
+ import clsx from 'clsx'
11
+ import { Currency } from '@licklist/core/dist/Config'
12
+ import { useIntl } from 'react-intl'
13
+ import { MAX_PRODUCT_NUMBER } from 'src/iframe/order-process/components/CategoryProduct/constants'
14
+ import { NumberInputHorizontal } from 'src/sales'
15
+ import { selectModifierType } from '../../utils'
16
+
17
+ interface ModifiersSetControllProps {
18
+ modifiers?: Modifier[]
19
+ modifierSet: ModifierSet
20
+ isLoading?: boolean
21
+ isEditMode?: boolean
22
+ productId: number
23
+ orderProductModifierSets?: OrderModifier[]
24
+ }
25
+
26
+ export const ModifiersSetControl = ({
27
+ modifierSet,
28
+ modifiers,
29
+ productId,
30
+ isLoading = false,
31
+ isEditMode = false,
32
+ orderProductModifierSets,
33
+ }: ModifiersSetControllProps) => {
34
+ const { control, watch, setValue, trigger } = useFormContext()
35
+ const { t } = useTranslation('App')
36
+
37
+ const orderModifiersSets = watch(`${productId}.orderProductModifiers`)
38
+
39
+ const { formatNumber } = useIntl()
40
+ const formatToCurrency = (value: number) =>
41
+ formatNumber(value, { style: 'currency', currency: Currency.GBP })
42
+
43
+ const currentOrderModifiersSets = useMemo(
44
+ () =>
45
+ orderProductModifierSets?.filter(
46
+ (modifier) => modifier.modifierSetId === modifierSet.id,
47
+ ) || [],
48
+ [orderProductModifierSets, modifierSet.id],
49
+ )
50
+
51
+ const onChangeModifiers = (
52
+ e: ChangeEvent<HTMLInputElement>,
53
+ type: selectModifierType,
54
+ quantity?: number,
55
+ ) => {
56
+ if (!modifiers) return
57
+
58
+ const selectedModifier = modifiers?.find(
59
+ (item) => item.id === Number(e.target.defaultValue || e.target.id),
60
+ )
61
+ const checkSelectedModifier =
62
+ orderModifiersSets?.find(
63
+ (modifier) => modifier?.modifierId === selectedModifier?.id,
64
+ )?.quantity === 1
65
+
66
+ if (selectedModifier && type === 'radio') {
67
+ modifiers.forEach((modifier) => {
68
+ const isSelected = modifier.id === selectedModifier.id
69
+ const orderModifiers: OrderModifier = {
70
+ modifierId: modifier.id,
71
+ price: modifier.price,
72
+ productId,
73
+ name: modifier.name,
74
+ quantity: isSelected ? 1 : 0,
75
+ modifierSetId: modifierSet.id,
76
+ }
77
+ setValue(
78
+ `${productId}.orderProductModifiers.${modifierSet.id}`,
79
+ orderModifiers,
80
+ )
81
+ })
82
+ }
83
+
84
+ if (selectedModifier && type === 'checkbox') {
85
+ const orderModifiers: OrderModifier = {
86
+ modifierId: selectedModifier.id,
87
+ price: selectedModifier.price,
88
+ productId,
89
+ name: selectedModifier.name,
90
+ quantity: checkSelectedModifier ? 0 : 1,
91
+ modifierSetId: modifierSet.id,
92
+ }
93
+ setValue(
94
+ `${productId}.orderProductModifiers.${modifierSet.id}.${selectedModifier.id}`,
95
+ orderModifiers,
96
+ )
97
+ }
98
+
99
+ if (selectedModifier && type === 'selector' && quantity) {
100
+ const orderModifiers: OrderModifier = {
101
+ modifierId: selectedModifier.id,
102
+ price: selectedModifier.price,
103
+ productId,
104
+ quantity,
105
+ name: selectedModifier.name,
106
+ modifierSetId: modifierSet.id,
107
+ }
108
+ setValue(
109
+ `${productId}.orderProductModifiers.${modifierSet.id}.${selectedModifier.id}`,
110
+ orderModifiers,
111
+ )
112
+ }
113
+
114
+ trigger(`${productId}.orderProductModifiers`)
115
+ }
116
+
117
+ useEffect(() => {
118
+ if (!orderProductModifierSets?.length) return
119
+ orderProductModifierSets.forEach((orderModifier) =>
120
+ setValue(
121
+ `${productId}.orderProductModifiers.${orderModifier.modifierSetId}.${orderModifier.modifierId}`,
122
+ orderModifier,
123
+ ),
124
+ )
125
+ }, [isEditMode])
126
+
127
+ return (
128
+ <>
129
+ {modifierSet?.maxItems === 1 &&
130
+ modifiers.every((modifier) => modifier.maxItems === 1) &&
131
+ !currentOrderModifiersSets?.find((modifier) => !!modifier?.quantity) ? (
132
+ <>
133
+ <Controller
134
+ name={`${productId}.orderProductModifiers.${modifierSet.id}`}
135
+ control={control}
136
+ rules={{
137
+ required: {
138
+ value: !!modifierSet.minItems,
139
+ message: t('Validation:fieldRequired') as string,
140
+ },
141
+ }}
142
+ render={({ field, fieldState }) => {
143
+ const onChange = (e: ChangeEvent<HTMLInputElement>) => {
144
+ onChangeModifiers(e, 'radio')
145
+ field.onChange(e.target.id)
146
+ }
147
+ return (
148
+ <>
149
+ {modifiers.map((modifier) => {
150
+ const selectedOrderModifierId = currentOrderModifiersSets
151
+ ?.find(
152
+ (orderModifier) =>
153
+ orderModifier.modifierId === modifier.id,
154
+ )
155
+ ?.modifierId?.toString()
156
+
157
+ return (
158
+ <Col key={modifier.id}>
159
+ <div className='modifier-container d-flex flex-column p-2'>
160
+ <Form.Check
161
+ className='custom-radio p-2'
162
+ inline
163
+ id={modifier.id.toString()}
164
+ defaultChecked={!!selectedOrderModifierId}
165
+ defaultValue={modifier.id.toString()}
166
+ checked={field.value === modifier.id.toString()}
167
+ onChange={onChange}
168
+ type='radio'
169
+ label={modifier.name}
170
+ name={modifier.name}
171
+ />
172
+ <div className='mt-3 w-100 pl-2'>
173
+ <p>{modifier.description}</p>
174
+ <div className='price'>
175
+ + {formatToCurrency(modifier.price)}
176
+ </div>
177
+ </div>
178
+ </div>
179
+ </Col>
180
+ )
181
+ })}
182
+ {fieldState.error && (
183
+ <div className='invalid-feedback d-block'>
184
+ {fieldState.error.message}
185
+ </div>
186
+ )}
187
+ </>
188
+ )
189
+ }}
190
+ />
191
+ </>
192
+ ) : (
193
+ <>
194
+ {modifiers?.map((modifier, index) => {
195
+ const selectedOrderModifier = currentOrderModifiersSets?.find(
196
+ (orderModifier) => orderModifier.modifierId === modifier.id,
197
+ )?.quantity
198
+
199
+ return (
200
+ <>
201
+ {modifier.maxItems === 1 ? (
202
+ <>
203
+ <Controller
204
+ name={`${productId}.${modifierSet.id}.modifiers[${index}]`}
205
+ control={control}
206
+ rules={{
207
+ required: {
208
+ value: !!modifier.minItems,
209
+ message: t('Validation:fieldRequired') as string,
210
+ },
211
+ validate: (value) => {
212
+ const { maxItems, minItems } = modifier
213
+ if (value?.length > maxItems) {
214
+ return t('Validation:quantityMaxNumberModifier', {
215
+ max: maxItems,
216
+ }) as string
217
+ }
218
+
219
+ if (value?.length < minItems) {
220
+ return t('Validation:quantityMinNumberModifier', {
221
+ min: minItems,
222
+ }) as string
223
+ }
224
+
225
+ return true
226
+ },
227
+ }}
228
+ render={({ field, fieldState: { error } }) => {
229
+ const onChange = (e: ChangeEvent<HTMLInputElement>) => {
230
+ onChangeModifiers(e, 'checkbox')
231
+ field.onChange(
232
+ xor(field.value, [Number(e.target.value)]),
233
+ )
234
+ }
235
+
236
+ return (
237
+ <Col
238
+ className='modifier-container pl-4'
239
+ key={modifier.id.toString()}
240
+ >
241
+ <div className='d-flex justify-content-between pl-2 align-items-center'>
242
+ <Checkbox
243
+ value={modifier.id}
244
+ onChange={onChange}
245
+ label={modifier.name}
246
+ defaultChecked={!!selectedOrderModifier}
247
+ />
248
+ </div>
249
+ <div className='mt-3 w-100 pl-2'>
250
+ <p>{modifier.description}</p>
251
+ <div className='price'>
252
+ + {formatToCurrency(modifier.price)}
253
+ </div>
254
+ </div>
255
+ {error?.message && (
256
+ <div className='invalid-feedback d-flex'>
257
+ {error?.message}
258
+ </div>
259
+ )}
260
+ </Col>
261
+ )
262
+ }}
263
+ />
264
+ </>
265
+ ) : (
266
+ <>
267
+ <div className='modifier-header m-0'>
268
+ <div className='title'>{modifier.name}</div>
269
+ </div>
270
+ <Controller
271
+ control={control}
272
+ name={`${productId}.${modifierSet.id}.modifiers[${index}].quantity`}
273
+ rules={{
274
+ required: {
275
+ value: !!modifier.minItems,
276
+ message: t('Validation:fieldRequired') as string,
277
+ },
278
+ validate: (value) => {
279
+ const { maxItems, minItems } = modifier
280
+ if (value > maxItems) {
281
+ return t('Validation:quantityMaxNumberModifier', {
282
+ max: maxItems,
283
+ }) as string
284
+ }
285
+
286
+ if (value < minItems) {
287
+ return t('Validation:quantityMinNumberModifier', {
288
+ min: minItems,
289
+ }) as string
290
+ }
291
+
292
+ return true
293
+ },
294
+ }}
295
+ render={({
296
+ field: { onChange, value, ref },
297
+ fieldState: { invalid, error },
298
+ }) => {
299
+ const onChangeProductQuantity = (val: number) => {
300
+ const quantity = Math.min(
301
+ MAX_PRODUCT_NUMBER,
302
+ Math.max(0, Math.ceil(val)),
303
+ )
304
+ onChange(quantity)
305
+ trigger(
306
+ `${productId}.${modifierSet.id}.modifiers[${index}].quantity`,
307
+ )
308
+ onChangeModifiers(
309
+ { target: { id: modifier.id.toString() } },
310
+ 'selector',
311
+ quantity,
312
+ )
313
+ }
314
+
315
+ return (
316
+ <div className='modifier-container'>
317
+ <div className='d-flex align-items-center column mb-8 pl-6 pt-2'>
318
+ <div className='quantity-container mb-0'>
319
+ <NumberInputHorizontal
320
+ ref={ref}
321
+ size='big'
322
+ onChange={onChangeProductQuantity}
323
+ onMinusClick={onChangeProductQuantity}
324
+ onPlusClick={onChangeProductQuantity}
325
+ min={0}
326
+ max={MAX_PRODUCT_NUMBER}
327
+ value={value ?? selectedOrderModifier ?? 0}
328
+ className={clsx(invalid && 'error')}
329
+ isLoading={isLoading}
330
+ />
331
+ </div>
332
+ </div>
333
+ <div className='p-2 mt-3 w-100 pl-4'>
334
+ <p className='pl-2'>{modifier.description}</p>
335
+ <div className='pl-2 price'>
336
+ + {formatToCurrency(modifier.price)}
337
+ </div>
338
+ {error?.message && (
339
+ <div className='d-flex mt-3 w-100'>
340
+ <p className='iframe-event__message-error'>
341
+ {error.message}
342
+ </p>
343
+ </div>
344
+ )}
345
+ </div>
346
+ </div>
347
+ )
348
+ }}
349
+ />
350
+ </>
351
+ )}
352
+ </>
353
+ )
354
+ })}
355
+ </>
356
+ )}
357
+ </>
358
+ )
359
+ }
360
+
361
+ type CheckboxProps = {
362
+ onChange: (e: ChangeEvent<HTMLInputElement>) => void
363
+ value: number
364
+ label: string
365
+ defaultChecked: boolean
366
+ }
367
+
368
+ const Checkbox = ({
369
+ onChange,
370
+ value,
371
+ label,
372
+ defaultChecked,
373
+ }: CheckboxProps) => {
374
+ const checkboxId = useId()
375
+
376
+ return (
377
+ <Form.Group controlId={checkboxId} className='custom-checkbox mt-0'>
378
+ <Form.Check
379
+ label={label}
380
+ value={value}
381
+ defaultChecked={defaultChecked}
382
+ onChange={onChange}
383
+ type='checkbox'
384
+ />
385
+ </Form.Group>
386
+ )
387
+ }
@@ -0,0 +1,96 @@
1
+ import { useEffect } from 'react'
2
+ import { useFormContext, Controller } from 'react-hook-form'
3
+ import { OrderModifierByProduct } from '@licklist/core/dist/DataMapper/Order/OrderModifiierByProduct'
4
+ import { Product } from '@licklist/plugins/dist/types/context/sale/menuSteps'
5
+ import { useTranslation } from 'react-i18next' // Added translation hook
6
+ import { ModifiersSetControl } from './ModifierSetControll'
7
+ import { orderProductModifiersQuantity } from '../../utils'
8
+
9
+ interface ProductControlProps {
10
+ product: Product
11
+ isLoading?: boolean
12
+ editOrderModifier?: OrderModifierByProduct
13
+ }
14
+
15
+ export const ProductControl = ({
16
+ product,
17
+ isLoading = false,
18
+ editOrderModifier,
19
+ }: ProductControlProps) => {
20
+ const { t } = useTranslation() // Added translation hook
21
+ const {
22
+ control,
23
+ watch,
24
+ formState: { isValid },
25
+ clearErrors,
26
+ } = useFormContext()
27
+ const modifiersSet = product?.modifiersSet || []
28
+ const orderModifiersSets = watch(`${product.id}.orderProductModifiers`)
29
+
30
+ useEffect(() => {
31
+ if (!isValid) return
32
+ clearErrors()
33
+ }, [isValid, clearErrors])
34
+
35
+ return (
36
+ <div className='mb-8 pt-4'>
37
+ <div className='d-flex flex-column'>
38
+ {modifiersSet.map((modifierSet) => {
39
+ const orderProductModifiersMaxQuantity =
40
+ orderProductModifiersQuantity(orderModifiersSets, modifierSet)
41
+
42
+ return (
43
+ <div className='modifier-set-container' key={modifierSet.id}>
44
+ <div className='modifier-header'>
45
+ <div className='title'>{modifierSet.name}</div>
46
+ </div>
47
+ <Controller
48
+ control={control}
49
+ name={`${product.id}.${modifierSet.id}`}
50
+ rules={{
51
+ validate: () => {
52
+ const { maxItems, minItems } = modifierSet
53
+
54
+ if (
55
+ !!minItems &&
56
+ orderProductModifiersMaxQuantity < minItems
57
+ ) {
58
+ return t('Validation:quantityMinNumberModifier', {
59
+ min: minItems,
60
+ }) as string
61
+ }
62
+
63
+ if (orderProductModifiersMaxQuantity > maxItems) {
64
+ return t('Validation:quantityMaxNumberModifier', {
65
+ max: maxItems,
66
+ }) as string
67
+ }
68
+
69
+ return true
70
+ },
71
+ }}
72
+ render={({ fieldState: { error } }) => (
73
+ <>
74
+ <ModifiersSetControl
75
+ modifierSet={modifierSet}
76
+ productId={product.id}
77
+ modifiers={modifierSet.modifiers}
78
+ isEditMode={!!editOrderModifier?.modifiers}
79
+ orderProductModifierSets={editOrderModifier?.modifiers}
80
+ isLoading={isLoading}
81
+ />
82
+ {error?.message && (
83
+ <div className='invalid-feedback d-flex pl-4'>
84
+ {error?.message}!
85
+ </div>
86
+ )}
87
+ </>
88
+ )}
89
+ />
90
+ </div>
91
+ )
92
+ })}
93
+ </div>
94
+ </div>
95
+ )
96
+ }
@@ -0,0 +1 @@
1
+ import './ProductWithModifierSetModal.js'
@@ -0,0 +1 @@
1
+ export { ProductWithModifierSetModal } from './ModifierSetModal/ProductWithModifierSetModal'
@@ -0,0 +1,28 @@
1
+ import { OrderModifier } from "@licklist/core/dist/DataMapper/Order/OrderModifierDataMapper"
2
+ import { uniqBy } from "lodash"
3
+
4
+ export type selectModifierType = 'radio' | 'checkbox' | 'selector'
5
+
6
+ export const filteredOrderModifierSets = (
7
+ modifiersSet: OrderModifier[],
8
+ ): OrderModifier[] => {
9
+ if (!modifiersSet) return []
10
+ const orderModifier = modifiersSet
11
+ .flat()
12
+ .filter((modifier) => !!modifier?.quantity)
13
+
14
+ return uniqBy(
15
+ orderModifier,
16
+ (item) => `${item.modifierId}-${item.modifierSetId}`,
17
+ )
18
+ }
19
+
20
+ export const orderProductModifiersQuantity = (
21
+ orderModifiersSets: OrderModifier[],
22
+ modifierSet,
23
+ ) => {
24
+ const filteredOrderModifier = filteredOrderModifierSets(orderModifiersSets)
25
+ return filteredOrderModifier.filter(
26
+ (item) => item?.modifierSetId === modifierSet.id,
27
+ ).length
28
+ }
@@ -12,6 +12,7 @@ interface TicketDescriptionProps {
12
12
  title: string
13
13
  description: string
14
14
  className?: string
15
+ classNameProductModal?: string
15
16
  images: Image[] | null
16
17
  price?: ReactNode
17
18
  isRequired?: boolean
@@ -24,6 +25,7 @@ export function TicketDescription({
24
25
  title,
25
26
  description,
26
27
  className = '',
28
+ classNameProductModal = '',
27
29
  images,
28
30
  price,
29
31
  isRequired = false,
@@ -47,10 +49,10 @@ export function TicketDescription({
47
49
  className='ticket-description__wrapper'
48
50
  style={descriptionWrapperStyle}
49
51
  >
50
- <div className='ticket-description__wrapper-children'>
52
+ <div className={clsx('ticket-description__wrapper-children', classNameProductModal)}>
51
53
  {imageSrc && (
52
- <div className='image-wrapper'>
53
- <img src={imageSrc} alt={title} />
54
+ <div className={clsx('image-wrapper', classNameProductModal && 'w-100 h-25')}>
55
+ <img className={clsx(classNameProductModal && 'w-100')} src={imageSrc} alt={title} />
54
56
  </div>
55
57
  )}
56
58
  {canExpand && description.length > DESCRIPTION_MAX_LENGTH ? (
@@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next'
2
2
  import { useIntl } from 'react-intl'
3
3
  import * as Config from '@licklist/core/dist/Config'
4
4
  import { OrderItem } from '@licklist/plugins/dist/types/context/Iframe/orderItems'
5
+ import { calculateTotalModifiersPrice } from '../../utils'
5
6
 
6
7
  type ProductSummaryProps = {
7
8
  name?: string
@@ -19,11 +20,32 @@ export const ProductSummary = ({
19
20
 
20
21
  const priceForOneProduct = orderProduct?.price
21
22
 
23
+ const modifierSetsPrice = calculateTotalModifiersPrice(
24
+ orderProduct?.orderModifiersSets,
25
+ )
26
+
22
27
  const fullPrice = formatNumber(priceForOneProduct * orderProduct?.quantity, {
23
28
  style: 'currency',
24
29
  currency: Config.Currency.GBP,
25
30
  })
26
31
 
32
+ const modifiersName = `${orderProduct.orderModifiersSets
33
+ ?.map((modifierProduct) =>
34
+ modifierProduct?.modifiers
35
+ .map((item) =>
36
+ item?.quantity > 1
37
+ ? `${item?.quantity}x - ${item?.name}`
38
+ : item?.name,
39
+ )
40
+ .join(', '),
41
+ )
42
+ .join(', ')}`
43
+
44
+ const fullModifiersPrice = formatNumber(modifierSetsPrice, {
45
+ style: 'currency',
46
+ currency: Config.Currency.GBP,
47
+ })
48
+
27
49
  return (
28
50
  <div className='product'>
29
51
  <p className='m-0 name'>{name ?? orderProduct?.name}</p>
@@ -32,9 +54,18 @@ export const ProductSummary = ({
32
54
  {t('shortQuantity')}:&nbsp;{orderProduct?.quantity}
33
55
  </p>
34
56
 
35
- <p className='price'>{fullPrice}</p>
57
+ {modifierSetsPrice && <p className='price'>{fullPrice}</p>}
36
58
  </div>
37
59
 
60
+ {fullModifiersPrice && (
61
+ <>
62
+ <p className='mt-2'>{modifiersName}</p>
63
+ <div className='d-flex justify-content-end'>
64
+ <p className='price'>{fullModifiersPrice}</p>
65
+ </div>
66
+ </>
67
+ )}
68
+
38
69
  {productQuantityError && (
39
70
  <p className='iframe-event__message-error'>{productQuantityError}</p>
40
71
  )}
@@ -1,3 +1,7 @@
1
+ import { OrderModifier } from '@licklist/core/dist/DataMapper/Order/OrderModifierDataMapper'
2
+ import {
3
+ OrderModifierByProduct
4
+ } from '@licklist/core/dist/DataMapper/Order/OrderModifiierByProduct'
1
5
  import { QuantityCheckProductInfo } from '@licklist/plugins/dist/types/Api/verifyStock'
2
6
  import { OrderItem } from '@licklist/plugins/dist/types/context/Iframe/orderItems'
3
7
 
@@ -14,16 +18,56 @@ export const cartSumByOrderProducts = ({
14
18
  return orderProducts.reduce((prevSumValue: number, product) => {
15
19
  if (!product) return 0
16
20
 
21
+ const modifiersPrice = calculateTotalModifiersPrice(
22
+ product?.orderModifiersSets,
23
+ )
24
+
17
25
  const price = isTotalSum
18
26
  ? product?.price
19
27
  : product.hasDeposit
20
28
  ? product?.deposit
21
29
  : product?.price
22
30
 
23
- return prevSumValue + price * product.quantity
31
+ return prevSumValue + price * product.quantity + modifiersPrice
24
32
  }, 0)
25
33
  }
26
34
 
35
+ export const getModifierName = (
36
+ modifier: OrderModifierByProduct,
37
+ isManualBooking: boolean,
38
+ ) => {
39
+ const modifierName = (orderModifier: OrderModifier) =>
40
+ !isManualBooking ? orderModifier?.name : orderModifier.modifier.name
41
+ return `${modifier.modifiers.map((item) => (
42
+ item?.quantity > 1 ? `${item?.quantity}x - ${modifierName(item)}` : modifierName(item))).join(', ')}`
43
+ }
44
+
45
+ export const calculateTotalModifiersPrice = (
46
+ orderModifiersSets: OrderModifierByProduct[],
47
+ ) =>
48
+ orderModifiersSets
49
+ ?.map((set) =>
50
+ set.modifiers.reduce(
51
+ (sum, modifier) =>
52
+ sum + modifier.price * modifier.quantity * set.productQuantity,
53
+ 0,
54
+ ),
55
+ )
56
+ .reduce((total, setTotal) => total + setTotal, 0)
57
+
58
+ export const getOrderProductModifiersPrice = (
59
+ modifierFromProduct: OrderModifierByProduct,
60
+ ) => {
61
+ const { modifiers, productQuantity } = modifierFromProduct
62
+ const modifiersPrices = modifiers.reduce(
63
+ (prevSumModifier: number, modifier: OrderModifier) =>
64
+ prevSumModifier + modifier.price * modifier.quantity,
65
+ 0,
66
+ )
67
+
68
+ return modifiersPrices * productQuantity
69
+ }
70
+
27
71
  export const getProductError = (
28
72
  productsWithErrors: QuantityCheckProductInfo[],
29
73
  productId: OrderItem['id'],