tf-checkout-react 1.7.13 → 1.7.15
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.
- package/dist/components/addonsContainer/AddonComponent.d.ts +2 -1
- package/dist/components/addonsContainer/SimpleAddonsContainer.d.ts +2 -1
- package/dist/components/addonsContainer/index.d.ts +2 -4
- package/dist/components/addonsContainer/useAddons.d.ts +23 -0
- package/dist/index.d.ts +3 -0
- package/dist/tf-checkout-react.cjs.development.js +237 -138
- package/dist/tf-checkout-react.cjs.development.js.map +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
- package/dist/tf-checkout-react.esm.js +236 -140
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/addonsContainer/AddonComponent.tsx +56 -14
- package/src/components/addonsContainer/SimpleAddonsContainer.tsx +3 -0
- package/src/components/addonsContainer/index.tsx +29 -230
- package/src/components/addonsContainer/useAddons.ts +248 -0
- package/src/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Field } from 'formik'
|
|
2
|
+
import { Field, useFormikContext } from 'formik'
|
|
3
3
|
import _identity from 'lodash/identity'
|
|
4
4
|
import _isEmpty from 'lodash/isEmpty'
|
|
5
5
|
import _isNull from 'lodash/isNull'
|
|
@@ -13,10 +13,6 @@ import {
|
|
|
13
13
|
} from '../billing-info-container/utils'
|
|
14
14
|
import { NativeSelectField } from '../common'
|
|
15
15
|
|
|
16
|
-
// TO DO:
|
|
17
|
-
// - Need to add custom fields validations
|
|
18
|
-
// - Need to apply correct styles
|
|
19
|
-
|
|
20
16
|
interface IAddonComponentProps {
|
|
21
17
|
classNamePrefix: string;
|
|
22
18
|
data: any;
|
|
@@ -26,6 +22,7 @@ interface IAddonComponentProps {
|
|
|
26
22
|
configs: any;
|
|
27
23
|
values: any;
|
|
28
24
|
errors: any;
|
|
25
|
+
useStepperQty?: boolean;
|
|
29
26
|
onCustomFieldChange?: (
|
|
30
27
|
addon: any,
|
|
31
28
|
groupId: string | number,
|
|
@@ -34,6 +31,41 @@ interface IAddonComponentProps {
|
|
|
34
31
|
) => void;
|
|
35
32
|
}
|
|
36
33
|
|
|
34
|
+
const AddonStepper = ({ id, selectOptions, handleAddonChange, classNamePrefix }: any) => {
|
|
35
|
+
const { values, setFieldValue } = useFormikContext<any>()
|
|
36
|
+
const qty = Number(values[id] ?? 0)
|
|
37
|
+
const max = selectOptions?.length > 0 ? selectOptions[selectOptions.length - 1].value : 0
|
|
38
|
+
|
|
39
|
+
const change = (next: number) => {
|
|
40
|
+
setFieldValue(id, next)
|
|
41
|
+
handleAddonChange(id, next)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className={`${classNamePrefix}_stepper`}>
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
className={`${classNamePrefix}_stepper__btn`}
|
|
49
|
+
onClick={() => change(qty - 1)}
|
|
50
|
+
disabled={qty <= 0}
|
|
51
|
+
aria-label="Decrease quantity"
|
|
52
|
+
>
|
|
53
|
+
−
|
|
54
|
+
</button>
|
|
55
|
+
<span className={`${classNamePrefix}_stepper__count`}>{qty}</span>
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
className={`${classNamePrefix}_stepper__btn`}
|
|
59
|
+
onClick={() => change(qty + 1)}
|
|
60
|
+
disabled={qty >= max}
|
|
61
|
+
aria-label="Increase quantity"
|
|
62
|
+
>
|
|
63
|
+
+
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
37
69
|
const AddonComponent = ({
|
|
38
70
|
classNamePrefix = '',
|
|
39
71
|
data,
|
|
@@ -43,6 +75,7 @@ const AddonComponent = ({
|
|
|
43
75
|
configs,
|
|
44
76
|
values,
|
|
45
77
|
errors,
|
|
78
|
+
useStepperQty = false,
|
|
46
79
|
onCustomFieldChange = _identity,
|
|
47
80
|
}: IAddonComponentProps) => {
|
|
48
81
|
const { id, name, active, stock } = data
|
|
@@ -57,15 +90,24 @@ const AddonComponent = ({
|
|
|
57
90
|
) : (
|
|
58
91
|
<>
|
|
59
92
|
<div className={`${classNamePrefix}_product_qty_select`}>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
93
|
+
{useStepperQty ? (
|
|
94
|
+
<AddonStepper
|
|
95
|
+
id={id}
|
|
96
|
+
selectOptions={selectOptions}
|
|
97
|
+
handleAddonChange={handleAddonChange}
|
|
98
|
+
classNamePrefix={classNamePrefix}
|
|
99
|
+
/>
|
|
100
|
+
) : (
|
|
101
|
+
<Field
|
|
102
|
+
name={id}
|
|
103
|
+
selectOptions={selectOptions}
|
|
104
|
+
component={NativeSelectField}
|
|
105
|
+
onChange={(e: any) => {
|
|
106
|
+
const { value } = e.target
|
|
107
|
+
handleAddonChange(id, value)
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
)}
|
|
69
111
|
</div>
|
|
70
112
|
{!_isEmpty(addOnDataWithCustomFields?.fields) && values[id] > 0 && (
|
|
71
113
|
<div className="add-on-fields">
|
|
@@ -30,6 +30,7 @@ export interface ISimpleAddonContainerProps {
|
|
|
30
30
|
addOnDataWithCustomFields: any;
|
|
31
31
|
configs: any;
|
|
32
32
|
eventId: string;
|
|
33
|
+
useStepperQty?: boolean;
|
|
33
34
|
onAddOnSelect?: (id: string, value: string, addon: any, fieldUpdates: any) => void;
|
|
34
35
|
handleConfirm?: (values: any) => void;
|
|
35
36
|
}
|
|
@@ -46,6 +47,7 @@ export const SimpleAddonsContainer = ({
|
|
|
46
47
|
addOnDataWithCustomFields,
|
|
47
48
|
configs,
|
|
48
49
|
eventId,
|
|
50
|
+
useStepperQty = false,
|
|
49
51
|
onAddOnSelect = _identity,
|
|
50
52
|
handleConfirm = _identity,
|
|
51
53
|
}: ISimpleAddonContainerProps) => {
|
|
@@ -373,6 +375,7 @@ export const SimpleAddonsContainer = ({
|
|
|
373
375
|
configs={configs}
|
|
374
376
|
values={values}
|
|
375
377
|
errors={errors}
|
|
378
|
+
useStepperQty={useStepperQty}
|
|
376
379
|
/>
|
|
377
380
|
))
|
|
378
381
|
) : (
|
|
@@ -3,36 +3,21 @@
|
|
|
3
3
|
|
|
4
4
|
import { CircularProgress } from '@mui/material'
|
|
5
5
|
import { Form, Formik } from 'formik'
|
|
6
|
-
import _get from 'lodash/get'
|
|
7
6
|
import _identity from 'lodash/identity'
|
|
8
7
|
import _map from 'lodash/map'
|
|
9
|
-
import React, { useEffect,
|
|
8
|
+
import React, { useEffect, useState } from 'react'
|
|
10
9
|
import { Tooltip } from 'react-tooltip'
|
|
11
10
|
|
|
12
|
-
import { getAddons, getCart, getCheckoutPageConfigs, postOnCheckout } from '../../api'
|
|
13
11
|
import { FEES_STYLES } from '../../constants'
|
|
14
12
|
import { currencyNormalizerCreator } from '../../normalizers'
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
CONFIGS,
|
|
18
|
-
createCheckoutDataBodyWithDefaultHolder,
|
|
19
|
-
createMarkup,
|
|
20
|
-
getQueryVariable,
|
|
21
|
-
isBrowser,
|
|
22
|
-
} from '../../utils'
|
|
13
|
+
import { CONFIGS, createMarkup, getQueryVariable, isBrowser } from '../../utils'
|
|
23
14
|
import { VerificationPendingModal } from '../idVerificationContainer/VerificationPendingModal'
|
|
24
15
|
import InfoIcon from '../ticketsContainer/InfoIcon'
|
|
25
16
|
import TimerWidget from '../timerWidget'
|
|
26
|
-
import { addonsWithGroupsAdapter, cartAdapter } from './adapters'
|
|
27
17
|
import AddonComponent from './AddonComponent'
|
|
28
18
|
import { getNormalizedPrice } from './normalizers'
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
getAddonSelectOptions,
|
|
32
|
-
getSortedAddons,
|
|
33
|
-
getTicketRelatedAddons,
|
|
34
|
-
isAtLeastOneAddonSelected,
|
|
35
|
-
} from './utils'
|
|
19
|
+
import { isAtLeastOneAddonSelected } from './utils'
|
|
20
|
+
import { useAddons } from './useAddons'
|
|
36
21
|
|
|
37
22
|
export interface IAddonContainterProps {
|
|
38
23
|
classNamePrefix?: string;
|
|
@@ -52,11 +37,9 @@ export interface IAddonContainterProps {
|
|
|
52
37
|
configs: any;
|
|
53
38
|
|
|
54
39
|
onAddOnSelect?: (id: string, value: string, addon: any) => void;
|
|
40
|
+
useStepperQty?: boolean;
|
|
55
41
|
}
|
|
56
42
|
|
|
57
|
-
export interface ObjectLiteral {
|
|
58
|
-
[key: string]: any;
|
|
59
|
-
}
|
|
60
43
|
|
|
61
44
|
export const AddonsContainter = ({
|
|
62
45
|
classNamePrefix = 'add_on',
|
|
@@ -75,16 +58,30 @@ export const AddonsContainter = ({
|
|
|
75
58
|
addOnDataWithCustomFields,
|
|
76
59
|
configs,
|
|
77
60
|
onAddOnSelect = _identity,
|
|
61
|
+
useStepperQty = false,
|
|
78
62
|
}: IAddonContainterProps) => {
|
|
79
|
-
const eventId = getQueryVariable('event_id')
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
63
|
+
const eventId = getQueryVariable('event_id') || null
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
addons,
|
|
67
|
+
addonsOptions,
|
|
68
|
+
loading,
|
|
69
|
+
cartExpirationTime,
|
|
70
|
+
pendingVerificationMessage,
|
|
71
|
+
initialValues,
|
|
72
|
+
onFieldChange,
|
|
73
|
+
handleConfirm,
|
|
74
|
+
handleClearAddons,
|
|
75
|
+
} = useAddons(eventId, {
|
|
76
|
+
enableBillingInfoAutoCreate,
|
|
77
|
+
addOnDataWithCustomFields,
|
|
78
|
+
onGetAddonsPageInfoSuccess,
|
|
79
|
+
onGetAddonsPageInfoError,
|
|
80
|
+
onPostCheckoutSuccess,
|
|
81
|
+
onPostCheckoutError,
|
|
82
|
+
onConfirmSelectionSuccess,
|
|
83
|
+
onConfirmSelectionError,
|
|
84
|
+
})
|
|
88
85
|
|
|
89
86
|
const [visibleDescription, setVisibleDescription] = useState<string | null>(null)
|
|
90
87
|
|
|
@@ -96,207 +93,8 @@ export const AddonsContainter = ({
|
|
|
96
93
|
if (samePage) {
|
|
97
94
|
window.localStorage.removeItem('add_ons')
|
|
98
95
|
}
|
|
99
|
-
const getAddonsPageInfo = async () => {
|
|
100
|
-
try {
|
|
101
|
-
if (eventId) {
|
|
102
|
-
setLoading(true)
|
|
103
|
-
|
|
104
|
-
// Get choosed ticket info (id, count) from Cart request for addons options calculations
|
|
105
|
-
const cart = await getCart()
|
|
106
|
-
const { id: choosedTicketID, quantity, expiresAt } = cartAdapter(cart)
|
|
107
|
-
const choosedTicketCount = Number(quantity)
|
|
108
|
-
setCartExpirationTime(expiresAt)
|
|
109
|
-
|
|
110
|
-
// Get and collect addons data
|
|
111
|
-
const addonsData = await getAddons(eventId)
|
|
112
|
-
const adaptedAddons = addonsWithGroupsAdapter(addonsData)
|
|
113
|
-
const ticketRelatedAddons = getTicketRelatedAddons(
|
|
114
|
-
adaptedAddons,
|
|
115
|
-
choosedTicketID
|
|
116
|
-
)
|
|
117
|
-
const sortedTicketAddons = getSortedAddons(ticketRelatedAddons)
|
|
118
|
-
|
|
119
|
-
setAddons(sortedTicketAddons)
|
|
120
|
-
|
|
121
|
-
// Collect addons and addon group options
|
|
122
|
-
const {
|
|
123
|
-
addonsWithOptions,
|
|
124
|
-
groupsWithSelectedVariantsInfo,
|
|
125
|
-
groupsWithVariants,
|
|
126
|
-
} = getAddonSelectOptions(adaptedAddons, choosedTicketCount)
|
|
127
|
-
|
|
128
|
-
setAddonsOptions(addonsWithOptions)
|
|
129
|
-
setGroupsWithSelectedVariants(groupsWithSelectedVariantsInfo)
|
|
130
|
-
setGroupsWithInitialVariantsValues(groupsWithVariants)
|
|
131
|
-
|
|
132
|
-
// Success callback props
|
|
133
|
-
onGetAddonsPageInfoSuccess(addonsData)
|
|
134
|
-
}
|
|
135
|
-
} catch (e) {
|
|
136
|
-
// Callback error props
|
|
137
|
-
onGetAddonsPageInfoError(e)
|
|
138
|
-
} finally {
|
|
139
|
-
setLoading(false)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
getAddonsPageInfo()
|
|
144
96
|
}, [])
|
|
145
97
|
|
|
146
|
-
const recreateGroupVariantsSelectOptions = (groupId: any, changedGroupd: any) => {
|
|
147
|
-
const { choosedVariants, limit, selectedCount } = changedGroupd
|
|
148
|
-
const remainingGroupStock = limit - selectedCount
|
|
149
|
-
const recreatedVariantsOptions: ObjectLiteral = {}
|
|
150
|
-
|
|
151
|
-
// Regenerate variants allowed stock counts
|
|
152
|
-
for (const variant in choosedVariants) {
|
|
153
|
-
const variantId = variant
|
|
154
|
-
const variantCurrSelectedValue = choosedVariants[variant]
|
|
155
|
-
let allowedOptionCount
|
|
156
|
-
|
|
157
|
-
// Formula for regenerating
|
|
158
|
-
if (
|
|
159
|
-
remainingGroupStock >=
|
|
160
|
-
groupsWithInitialVariantsValues[groupId][variantId] - variantCurrSelectedValue
|
|
161
|
-
) {
|
|
162
|
-
allowedOptionCount = groupsWithInitialVariantsValues[groupId][variantId]
|
|
163
|
-
} else {
|
|
164
|
-
allowedOptionCount = remainingGroupStock + variantCurrSelectedValue
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
recreatedVariantsOptions[variantId] = generateSelectOptions(0, allowedOptionCount)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
setAddonsOptions((prevState: any) =>
|
|
171
|
-
Object.assign({}, prevState, recreatedVariantsOptions)
|
|
172
|
-
)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const onFieldChange = (id: any, value: any, addon: any) => {
|
|
176
|
-
// If changeableGroup exsists it means that group with limitation variant was changed
|
|
177
|
-
const changeableGroup = groupsWithSelectedVariants[addon.id]
|
|
178
|
-
|
|
179
|
-
if (changeableGroup) {
|
|
180
|
-
const currGroupId = addon.id
|
|
181
|
-
const currSelectedVariantId = id
|
|
182
|
-
const currSelectedVariantCount = Number(value)
|
|
183
|
-
const currSelectedVariantPrevCount =
|
|
184
|
-
groupsWithSelectedVariants[currGroupId].choosedVariants[currSelectedVariantId]
|
|
185
|
-
|
|
186
|
-
const currSelectedGroupCount =
|
|
187
|
-
changeableGroup.selectedCount +
|
|
188
|
-
(currSelectedVariantCount - currSelectedVariantPrevCount)
|
|
189
|
-
|
|
190
|
-
// Update Group info
|
|
191
|
-
const updatedGroupsWithSelectedVariants = {
|
|
192
|
-
...groupsWithSelectedVariants,
|
|
193
|
-
[currGroupId]: {
|
|
194
|
-
...groupsWithSelectedVariants[currGroupId],
|
|
195
|
-
selectedCount: currSelectedGroupCount,
|
|
196
|
-
choosedVariants: {
|
|
197
|
-
...groupsWithSelectedVariants[currGroupId].choosedVariants,
|
|
198
|
-
[currSelectedVariantId]: currSelectedVariantCount,
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
}
|
|
202
|
-
setGroupsWithSelectedVariants(updatedGroupsWithSelectedVariants)
|
|
203
|
-
|
|
204
|
-
// Recreate Select Options for Addon Group Variants
|
|
205
|
-
recreateGroupVariantsSelectOptions(
|
|
206
|
-
currGroupId,
|
|
207
|
-
updatedGroupsWithSelectedVariants[currGroupId]
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const handleConfirm = async (values: any, skipAddonPage?: boolean) => {
|
|
213
|
-
try {
|
|
214
|
-
const pageConfigsDataResponse = await getCheckoutPageConfigs()
|
|
215
|
-
const pageConfigsData: ICheckoutPageConfigs =
|
|
216
|
-
_get(pageConfigsDataResponse, 'data.attributes') || {}
|
|
217
|
-
|
|
218
|
-
const skipBillingPage = pageConfigsData.skip_billing_page ?? false
|
|
219
|
-
|
|
220
|
-
if (skipBillingPage && enableBillingInfoAutoCreate) {
|
|
221
|
-
const ticketsQuantity = window.localStorage.getItem('quantity')
|
|
222
|
-
const userData = JSON.parse(window.localStorage.getItem('user_data') || '{}')
|
|
223
|
-
|
|
224
|
-
const checkoutBody = createCheckoutDataBodyWithDefaultHolder(
|
|
225
|
-
Number(ticketsQuantity) || 0,
|
|
226
|
-
userData
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
const checkoutResponse = await postOnCheckout({
|
|
231
|
-
...checkoutBody,
|
|
232
|
-
attributes: {
|
|
233
|
-
...checkoutBody.attributes,
|
|
234
|
-
...(!skipAddonPage && { add_ons: values }),
|
|
235
|
-
},
|
|
236
|
-
})
|
|
237
|
-
const hash = checkoutResponse?.data?.attributes?.hash || ''
|
|
238
|
-
const total = checkoutResponse?.data?.attributes?.total || ''
|
|
239
|
-
|
|
240
|
-
isBrowser && window.localStorage.removeItem('quantity')
|
|
241
|
-
isBrowser && window.localStorage.removeItem('add_ons')
|
|
242
|
-
|
|
243
|
-
onPostCheckoutSuccess(checkoutResponse?.data.attributes)
|
|
244
|
-
onConfirmSelectionSuccess({
|
|
245
|
-
skip_billing_page: skipBillingPage,
|
|
246
|
-
event_id: String(eventId),
|
|
247
|
-
hash,
|
|
248
|
-
total,
|
|
249
|
-
})
|
|
250
|
-
} catch (error) {
|
|
251
|
-
if ((error as any).response?.data?.data?.hasUnverifiedOrder) {
|
|
252
|
-
setPendingVerificationMessage((error as any).response?.data?.message)
|
|
253
|
-
} else {
|
|
254
|
-
onPostCheckoutError(error)
|
|
255
|
-
onConfirmSelectionError(error)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
if (isBrowser) {
|
|
260
|
-
if (!skipAddonPage) {
|
|
261
|
-
window.localStorage.setItem('add_ons', JSON.stringify(values))
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
onConfirmSelectionSuccess({
|
|
265
|
-
skip_billing_page: skipBillingPage && enableBillingInfoAutoCreate,
|
|
266
|
-
event_id: String(eventId),
|
|
267
|
-
})
|
|
268
|
-
} else {
|
|
269
|
-
onConfirmSelectionError({
|
|
270
|
-
error: true,
|
|
271
|
-
message: 'Window is not defined',
|
|
272
|
-
})
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
} catch (e) {
|
|
276
|
-
onConfirmSelectionError(e)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const handleClearAddons = () => {
|
|
281
|
-
window.localStorage.removeItem('add_ons')
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const initialValues = useMemo(() => {
|
|
285
|
-
const addOnsData: any = {}
|
|
286
|
-
if (addons?.length > 0 && addOnDataWithCustomFields?.fields?.length > 0) {
|
|
287
|
-
_map(addons, addon => {
|
|
288
|
-
_map(addOnDataWithCustomFields.fields, field => {
|
|
289
|
-
const { id, groupItems } = field
|
|
290
|
-
_map(groupItems, item => {
|
|
291
|
-
addOnsData[`${addon.id}-${id}-${item.name}`] = item.value
|
|
292
|
-
})
|
|
293
|
-
})
|
|
294
|
-
})
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return addOnsData
|
|
298
|
-
}, [addons, addOnDataWithCustomFields])
|
|
299
|
-
|
|
300
98
|
if (loading) {
|
|
301
99
|
return (
|
|
302
100
|
<div className={`${classNamePrefix}_loader`}>
|
|
@@ -489,6 +287,7 @@ export const AddonsContainter = ({
|
|
|
489
287
|
configs={configs}
|
|
490
288
|
values={values}
|
|
491
289
|
errors={errors}
|
|
290
|
+
useStepperQty={useStepperQty}
|
|
492
291
|
/>
|
|
493
292
|
))
|
|
494
293
|
) : (
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import _get from 'lodash/get'
|
|
3
|
+
import _identity from 'lodash/identity'
|
|
4
|
+
import _map from 'lodash/map'
|
|
5
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
6
|
+
|
|
7
|
+
import { getAddons, getCart, getCheckoutPageConfigs, postOnCheckout } from '../../api'
|
|
8
|
+
import { ICheckoutPageConfigs } from '../../types'
|
|
9
|
+
import { createCheckoutDataBodyWithDefaultHolder, isBrowser } from '../../utils'
|
|
10
|
+
import { addonsWithGroupsAdapter, cartAdapter } from './adapters'
|
|
11
|
+
import {
|
|
12
|
+
generateSelectOptions,
|
|
13
|
+
getAddonSelectOptions,
|
|
14
|
+
getSortedAddons,
|
|
15
|
+
getTicketRelatedAddons,
|
|
16
|
+
} from './utils'
|
|
17
|
+
|
|
18
|
+
interface ObjectLiteral {
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface IUseAddonsOptions {
|
|
23
|
+
enableBillingInfoAutoCreate?: boolean;
|
|
24
|
+
addOnDataWithCustomFields?: any;
|
|
25
|
+
onGetAddonsPageInfoSuccess?: (res: any) => void;
|
|
26
|
+
onGetAddonsPageInfoError?: (error: any) => void;
|
|
27
|
+
onPostCheckoutSuccess?: (res: any) => void;
|
|
28
|
+
onPostCheckoutError?: (error: any) => void;
|
|
29
|
+
onConfirmSelectionSuccess?: (res: any) => void;
|
|
30
|
+
onConfirmSelectionError?: (error: any) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const useAddons = (eventId: string | null, options: IUseAddonsOptions = {}) => {
|
|
34
|
+
const {
|
|
35
|
+
enableBillingInfoAutoCreate = true,
|
|
36
|
+
addOnDataWithCustomFields,
|
|
37
|
+
onGetAddonsPageInfoSuccess = _identity,
|
|
38
|
+
onGetAddonsPageInfoError = _identity,
|
|
39
|
+
onPostCheckoutSuccess = _identity,
|
|
40
|
+
onPostCheckoutError = _identity,
|
|
41
|
+
onConfirmSelectionSuccess = _identity,
|
|
42
|
+
onConfirmSelectionError = _identity,
|
|
43
|
+
} = options
|
|
44
|
+
|
|
45
|
+
const [addons, setAddons] = useState<any>([])
|
|
46
|
+
const [addonsOptions, setAddonsOptions] = useState<any>({})
|
|
47
|
+
const [groupsWithSelectedVariants, setGroupsWithSelectedVariants] = useState<any>({})
|
|
48
|
+
const [groupsWithInitialVariantsValues, setGroupsWithInitialVariantsValues] = useState<any>({})
|
|
49
|
+
const [loading, setLoading] = useState(true)
|
|
50
|
+
const [cartExpirationTime, setCartExpirationTime] = useState(0)
|
|
51
|
+
const [pendingVerificationMessage, setPendingVerificationMessage] = useState<any>()
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const getAddonsPageInfo = async () => {
|
|
55
|
+
try {
|
|
56
|
+
if (eventId) {
|
|
57
|
+
setLoading(true)
|
|
58
|
+
|
|
59
|
+
const cart = await getCart()
|
|
60
|
+
const { id: choosedTicketID, quantity, expiresAt } = cartAdapter(cart)
|
|
61
|
+
const choosedTicketCount = Number(quantity)
|
|
62
|
+
setCartExpirationTime(expiresAt)
|
|
63
|
+
|
|
64
|
+
const addonsData = await getAddons(eventId)
|
|
65
|
+
const adaptedAddons = addonsWithGroupsAdapter(addonsData)
|
|
66
|
+
const ticketRelatedAddons = getTicketRelatedAddons(adaptedAddons, choosedTicketID)
|
|
67
|
+
const sortedTicketAddons = getSortedAddons(ticketRelatedAddons)
|
|
68
|
+
|
|
69
|
+
setAddons(sortedTicketAddons)
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
addonsWithOptions,
|
|
73
|
+
groupsWithSelectedVariantsInfo,
|
|
74
|
+
groupsWithVariants,
|
|
75
|
+
} = getAddonSelectOptions(adaptedAddons, choosedTicketCount)
|
|
76
|
+
|
|
77
|
+
setAddonsOptions(addonsWithOptions)
|
|
78
|
+
setGroupsWithSelectedVariants(groupsWithSelectedVariantsInfo)
|
|
79
|
+
setGroupsWithInitialVariantsValues(groupsWithVariants)
|
|
80
|
+
|
|
81
|
+
onGetAddonsPageInfoSuccess(addonsData)
|
|
82
|
+
}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
onGetAddonsPageInfoError(e)
|
|
85
|
+
} finally {
|
|
86
|
+
setLoading(false)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getAddonsPageInfo()
|
|
91
|
+
}, [eventId])
|
|
92
|
+
|
|
93
|
+
const recreateGroupVariantsSelectOptions = (groupId: any, changedGroup: any) => {
|
|
94
|
+
const { choosedVariants, limit, selectedCount } = changedGroup
|
|
95
|
+
const remainingGroupStock = limit - selectedCount
|
|
96
|
+
const recreatedVariantsOptions: ObjectLiteral = {}
|
|
97
|
+
|
|
98
|
+
for (const variant in choosedVariants) {
|
|
99
|
+
const variantId = variant
|
|
100
|
+
const variantCurrSelectedValue = choosedVariants[variant]
|
|
101
|
+
let allowedOptionCount
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
remainingGroupStock >=
|
|
105
|
+
groupsWithInitialVariantsValues[groupId][variantId] - variantCurrSelectedValue
|
|
106
|
+
) {
|
|
107
|
+
allowedOptionCount = groupsWithInitialVariantsValues[groupId][variantId]
|
|
108
|
+
} else {
|
|
109
|
+
allowedOptionCount = remainingGroupStock + variantCurrSelectedValue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
recreatedVariantsOptions[variantId] = generateSelectOptions(0, allowedOptionCount)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setAddonsOptions((prevState: any) => Object.assign({}, prevState, recreatedVariantsOptions))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const onFieldChange = (id: any, value: any, addon: any) => {
|
|
119
|
+
const changeableGroup = groupsWithSelectedVariants[addon.id]
|
|
120
|
+
|
|
121
|
+
if (changeableGroup) {
|
|
122
|
+
const currGroupId = addon.id
|
|
123
|
+
const currSelectedVariantId = id
|
|
124
|
+
const currSelectedVariantCount = Number(value)
|
|
125
|
+
const currSelectedVariantPrevCount =
|
|
126
|
+
groupsWithSelectedVariants[currGroupId].choosedVariants[currSelectedVariantId]
|
|
127
|
+
|
|
128
|
+
const currSelectedGroupCount =
|
|
129
|
+
changeableGroup.selectedCount +
|
|
130
|
+
(currSelectedVariantCount - currSelectedVariantPrevCount)
|
|
131
|
+
|
|
132
|
+
const updatedGroupsWithSelectedVariants = {
|
|
133
|
+
...groupsWithSelectedVariants,
|
|
134
|
+
[currGroupId]: {
|
|
135
|
+
...groupsWithSelectedVariants[currGroupId],
|
|
136
|
+
selectedCount: currSelectedGroupCount,
|
|
137
|
+
choosedVariants: {
|
|
138
|
+
...groupsWithSelectedVariants[currGroupId].choosedVariants,
|
|
139
|
+
[currSelectedVariantId]: currSelectedVariantCount,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
setGroupsWithSelectedVariants(updatedGroupsWithSelectedVariants)
|
|
144
|
+
|
|
145
|
+
recreateGroupVariantsSelectOptions(
|
|
146
|
+
currGroupId,
|
|
147
|
+
updatedGroupsWithSelectedVariants[currGroupId]
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const handleConfirm = async (values: any, skipAddonPage?: boolean) => {
|
|
153
|
+
try {
|
|
154
|
+
const pageConfigsDataResponse = await getCheckoutPageConfigs()
|
|
155
|
+
const pageConfigsData: ICheckoutPageConfigs =
|
|
156
|
+
_get(pageConfigsDataResponse, 'data.attributes') || {}
|
|
157
|
+
|
|
158
|
+
const skipBillingPage = pageConfigsData.skip_billing_page ?? false
|
|
159
|
+
|
|
160
|
+
if (skipBillingPage && enableBillingInfoAutoCreate) {
|
|
161
|
+
const ticketsQuantity = window.localStorage.getItem('quantity')
|
|
162
|
+
const userData = JSON.parse(window.localStorage.getItem('user_data') || '{}')
|
|
163
|
+
|
|
164
|
+
const checkoutBody = createCheckoutDataBodyWithDefaultHolder(
|
|
165
|
+
Number(ticketsQuantity) || 0,
|
|
166
|
+
userData
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const checkoutResponse = await postOnCheckout({
|
|
171
|
+
...checkoutBody,
|
|
172
|
+
attributes: {
|
|
173
|
+
...checkoutBody.attributes,
|
|
174
|
+
...(!skipAddonPage && { add_ons: values }),
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
const hash = checkoutResponse?.data?.attributes?.hash || ''
|
|
178
|
+
const total = checkoutResponse?.data?.attributes?.total || ''
|
|
179
|
+
|
|
180
|
+
isBrowser && window.localStorage.removeItem('quantity')
|
|
181
|
+
isBrowser && window.localStorage.removeItem('add_ons')
|
|
182
|
+
|
|
183
|
+
onPostCheckoutSuccess(checkoutResponse?.data.attributes)
|
|
184
|
+
onConfirmSelectionSuccess({
|
|
185
|
+
skip_billing_page: skipBillingPage,
|
|
186
|
+
event_id: String(eventId),
|
|
187
|
+
hash,
|
|
188
|
+
total,
|
|
189
|
+
})
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if ((error as any).response?.data?.data?.hasUnverifiedOrder) {
|
|
192
|
+
setPendingVerificationMessage((error as any).response?.data?.message)
|
|
193
|
+
} else {
|
|
194
|
+
onPostCheckoutError(error)
|
|
195
|
+
onConfirmSelectionError(error)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
if (isBrowser) {
|
|
200
|
+
if (!skipAddonPage) {
|
|
201
|
+
window.localStorage.setItem('add_ons', JSON.stringify(values))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
onConfirmSelectionSuccess({
|
|
205
|
+
skip_billing_page: skipBillingPage && enableBillingInfoAutoCreate,
|
|
206
|
+
event_id: String(eventId),
|
|
207
|
+
})
|
|
208
|
+
} else {
|
|
209
|
+
onConfirmSelectionError({ error: true, message: 'Window is not defined' })
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (e) {
|
|
213
|
+
onConfirmSelectionError(e)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const handleClearAddons = () => {
|
|
218
|
+
window.localStorage.removeItem('add_ons')
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const initialValues = useMemo(() => {
|
|
222
|
+
const addOnsData: any = {}
|
|
223
|
+
if (addons?.length > 0 && addOnDataWithCustomFields?.fields?.length > 0) {
|
|
224
|
+
_map(addons, addon => {
|
|
225
|
+
_map(addOnDataWithCustomFields.fields, field => {
|
|
226
|
+
const { id, groupItems } = field
|
|
227
|
+
_map(groupItems, item => {
|
|
228
|
+
addOnsData[`${addon.id}-${id}-${item.name}`] = item.value
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
return addOnsData
|
|
234
|
+
}, [addons, addOnDataWithCustomFields])
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
addons,
|
|
238
|
+
addonsOptions,
|
|
239
|
+
loading,
|
|
240
|
+
cartExpirationTime,
|
|
241
|
+
pendingVerificationMessage,
|
|
242
|
+
setPendingVerificationMessage,
|
|
243
|
+
initialValues,
|
|
244
|
+
onFieldChange,
|
|
245
|
+
handleConfirm,
|
|
246
|
+
handleClearAddons,
|
|
247
|
+
}
|
|
248
|
+
}
|