tf-checkout-react 1.2.30 → 1.3.0
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/api/index.d.ts +3 -0
- package/dist/components/addonsContainer/AddonComponent.d.ts +9 -0
- package/dist/components/addonsContainer/adapters/index.d.ts +8 -0
- package/dist/components/addonsContainer/index.d.ts +15 -0
- package/dist/components/addonsContainer/normalizers/index.d.ts +1 -0
- package/dist/components/addonsContainer/utils/index.d.ts +15 -0
- package/dist/components/common/SelectField.d.ts +2 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/ticketsContainer/index.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/tf-checkout-react.cjs.development.js +832 -80
- 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 +832 -81
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/add_on.d.ts +7 -0
- package/dist/types/checkoutPageConfigs.d.ts +9 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/order-data.d.ts +2 -1
- package/dist/utils/createMarkup.d.ts +3 -0
- package/dist/utils/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/api/index.ts +16 -0
- package/src/components/addonsContainer/AddonComponent.tsx +49 -0
- package/src/components/addonsContainer/adapters/index.tsx +61 -0
- package/src/components/addonsContainer/index.tsx +392 -0
- package/src/components/addonsContainer/normalizers/index.ts +5 -0
- package/src/components/addonsContainer/utils/index.tsx +207 -0
- package/src/components/billing-info-container/index.tsx +30 -7
- package/src/components/common/SelectField.tsx +18 -8
- package/src/components/index.ts +1 -0
- package/src/components/orderDetailsContainer/index.tsx +21 -18
- package/src/components/paymentContainer/index.tsx +32 -2
- package/src/components/ticketsContainer/TicketsSection.tsx +3 -3
- package/src/components/ticketsContainer/index.tsx +36 -17
- package/src/components/ticketsContainer/utils.ts +3 -3
- package/src/index.ts +2 -1
- package/src/types/add_on.ts +7 -0
- package/src/types/checkoutPageConfigs.ts +9 -0
- package/src/types/index.ts +2 -0
- package/src/types/order-data.ts +4 -3
- package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +7 -6
- package/src/utils/createMarkup.ts +1 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import _isNull from 'lodash/isNull'
|
|
2
|
+
import _reverse from 'lodash/reverse'
|
|
3
|
+
import _sortBy from 'lodash/sortBy'
|
|
4
|
+
|
|
5
|
+
interface ObjectLiteral {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const generateSelectOptions = (minCount = 1, maxCount = 10) => {
|
|
10
|
+
const options = []
|
|
11
|
+
for (let i = minCount; i <= maxCount; i++) {
|
|
12
|
+
options.push({ label: i, value: i })
|
|
13
|
+
}
|
|
14
|
+
return options
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const generateStockBasedOnLimitations = (
|
|
18
|
+
addon: any,
|
|
19
|
+
ticketQuantity: number
|
|
20
|
+
) => {
|
|
21
|
+
// Generate addon available stock count based on limitations
|
|
22
|
+
const { flagLimitToTicketQuantity, maxQuantity, limitPerTicket } = addon
|
|
23
|
+
|
|
24
|
+
let allowedStockCount
|
|
25
|
+
|
|
26
|
+
// Generate stock
|
|
27
|
+
if (flagLimitToTicketQuantity) {
|
|
28
|
+
// Limited to ticket quantity case
|
|
29
|
+
allowedStockCount = ticketQuantity
|
|
30
|
+
} else if (maxQuantity && limitPerTicket) {
|
|
31
|
+
const stockBasedOnLimitPerTicket = limitPerTicket * ticketQuantity
|
|
32
|
+
// Both maximum quantity and limited to per ticket selected case, stock is minimum of them
|
|
33
|
+
allowedStockCount =
|
|
34
|
+
maxQuantity <= stockBasedOnLimitPerTicket
|
|
35
|
+
? maxQuantity
|
|
36
|
+
: stockBasedOnLimitPerTicket
|
|
37
|
+
} else if (maxQuantity && !limitPerTicket) {
|
|
38
|
+
// Limited to maximum quantity case
|
|
39
|
+
allowedStockCount = maxQuantity
|
|
40
|
+
} else if (!maxQuantity && limitPerTicket) {
|
|
41
|
+
// Limited to per ticket case
|
|
42
|
+
allowedStockCount = limitPerTicket * ticketQuantity
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return Number(allowedStockCount)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const filterStockBasedOnAvailability = (
|
|
49
|
+
generatedStock: any,
|
|
50
|
+
availableStock: any
|
|
51
|
+
) => {
|
|
52
|
+
// Check generated stock count admissibility with addon stock availability
|
|
53
|
+
let filteredStockCount = generatedStock
|
|
54
|
+
|
|
55
|
+
if (generatedStock) {
|
|
56
|
+
if (generatedStock > availableStock && !_isNull(availableStock)) {
|
|
57
|
+
filteredStockCount = availableStock
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
// Not setted any restriction
|
|
61
|
+
if (_isNull(availableStock)) {
|
|
62
|
+
filteredStockCount = 10
|
|
63
|
+
} else {
|
|
64
|
+
filteredStockCount = availableStock
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return filteredStockCount
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const getAddonSelectOptions = (addons: any, choosedTicketCount: any) => {
|
|
72
|
+
const addonsWithOptions: ObjectLiteral = {}
|
|
73
|
+
const groupsWithSelectedVariantsInfo: ObjectLiteral = {}
|
|
74
|
+
const groupsWithVariants: ObjectLiteral = {}
|
|
75
|
+
|
|
76
|
+
addons.forEach((addon: any) => {
|
|
77
|
+
// Here addon can act either as simple Addon or Addon Group
|
|
78
|
+
const {
|
|
79
|
+
id,
|
|
80
|
+
stock: simpleAddonStock,
|
|
81
|
+
variants,
|
|
82
|
+
active,
|
|
83
|
+
flagLimitToTicketQuantity,
|
|
84
|
+
maxQuantity,
|
|
85
|
+
limitPerTicket,
|
|
86
|
+
} = addon
|
|
87
|
+
|
|
88
|
+
if (variants) {
|
|
89
|
+
// Addon Group with inside addon variants case
|
|
90
|
+
variants.forEach((variant: any) => {
|
|
91
|
+
const { id: variantId, stock: variantStock } = variant
|
|
92
|
+
|
|
93
|
+
// null checking is for unlimited stock value
|
|
94
|
+
if (active && (variantStock > 0 || _isNull(variantStock))) {
|
|
95
|
+
// Generate Addon Group allowed stock count based on limitations
|
|
96
|
+
const stockBasedOnLimitation = generateStockBasedOnLimitations(
|
|
97
|
+
addon,
|
|
98
|
+
choosedTicketCount
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
// Detect if group has limitation or not
|
|
102
|
+
if (flagLimitToTicketQuantity || maxQuantity || limitPerTicket) {
|
|
103
|
+
// Generate Group with inside variants info
|
|
104
|
+
if (groupsWithSelectedVariantsInfo[id]) {
|
|
105
|
+
// Set group limit
|
|
106
|
+
if (
|
|
107
|
+
groupsWithSelectedVariantsInfo[id].limit <
|
|
108
|
+
stockBasedOnLimitation
|
|
109
|
+
) {
|
|
110
|
+
groupsWithSelectedVariantsInfo[
|
|
111
|
+
id
|
|
112
|
+
].limit = stockBasedOnLimitation
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Set choosed variants info
|
|
116
|
+
groupsWithSelectedVariantsInfo[id] = {
|
|
117
|
+
...groupsWithSelectedVariantsInfo[id],
|
|
118
|
+
choosedVariants: {
|
|
119
|
+
...groupsWithSelectedVariantsInfo[id].choosedVariants,
|
|
120
|
+
[variantId]: 0,
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
groupsWithSelectedVariantsInfo[id] = {
|
|
125
|
+
limit: stockBasedOnLimitation,
|
|
126
|
+
selectedCount: 0,
|
|
127
|
+
choosedVariants: { [variantId]: 0 },
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check stock admissibility with addon stock availability
|
|
133
|
+
const allowedVariantStockCount = filterStockBasedOnAvailability(
|
|
134
|
+
stockBasedOnLimitation,
|
|
135
|
+
variantStock
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
// Generate options for variant
|
|
139
|
+
const variantOptions = generateSelectOptions(
|
|
140
|
+
0,
|
|
141
|
+
allowedVariantStockCount
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
addonsWithOptions[variantId] = [...variantOptions]
|
|
145
|
+
|
|
146
|
+
// Generate Group with its variants list
|
|
147
|
+
groupsWithVariants[id] = {
|
|
148
|
+
...groupsWithVariants[id],
|
|
149
|
+
[variantId]: allowedVariantStockCount,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
} else {
|
|
154
|
+
// Simple addon case, null checking is for unlimited stock value
|
|
155
|
+
if (active && (simpleAddonStock > 0 || _isNull(simpleAddonStock))) {
|
|
156
|
+
// Generate Addon Group allowed stock count based on limitations
|
|
157
|
+
const stockBasedOnLimitation = generateStockBasedOnLimitations(
|
|
158
|
+
addon,
|
|
159
|
+
choosedTicketCount
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// Check stock admissibility with addon stock availability
|
|
163
|
+
const allowedVariantStockCount = filterStockBasedOnAvailability(
|
|
164
|
+
stockBasedOnLimitation,
|
|
165
|
+
simpleAddonStock
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
const addonOptions = generateSelectOptions(0, allowedVariantStockCount)
|
|
169
|
+
addonsWithOptions[id] = [...addonOptions]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
addonsWithOptions,
|
|
176
|
+
groupsWithSelectedVariantsInfo,
|
|
177
|
+
groupsWithVariants,
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const getTicketRelatedAddons = (addons: any, ticketId: any) => {
|
|
182
|
+
// Filter addons based on choosed ticket
|
|
183
|
+
const filteredAddons: any = addons.filter((addon: any) => (
|
|
184
|
+
_isNull(addon.prerequisiteTicketTypeIds) || addon.prerequisiteTicketTypeIds.includes(ticketId)
|
|
185
|
+
))
|
|
186
|
+
|
|
187
|
+
return filteredAddons
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const getSortedAddons = (addons: any, sortDirection = "asc") => {
|
|
191
|
+
const addonsCopy = [...addons]
|
|
192
|
+
|
|
193
|
+
addonsCopy.forEach(addon => {
|
|
194
|
+
if (addon.variants) {
|
|
195
|
+
addon.sortOrder = Number(addon.variants[0].sortOrder)
|
|
196
|
+
} else {
|
|
197
|
+
addon.sortOrder = Number(addon.sortOrder)
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
const sortedAddons = _sortBy(addonsCopy, addon => addon.sortOrder)
|
|
202
|
+
if (sortDirection === "desc") {
|
|
203
|
+
return _reverse(sortedAddons)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return sortedAddons
|
|
207
|
+
}
|
|
@@ -310,6 +310,11 @@ export const BillingInfoContainer = React.memo(
|
|
|
310
310
|
// Get prevProps
|
|
311
311
|
const prevData = useRef(data)
|
|
312
312
|
|
|
313
|
+
const addAddOnsInAttributes = (checkoutBody: any) => {
|
|
314
|
+
const selectedAddOns = window.localStorage.getItem('add_ons') || '{}'
|
|
315
|
+
checkoutBody.attributes.add_ons = JSON.parse(selectedAddOns)
|
|
316
|
+
}
|
|
317
|
+
|
|
313
318
|
useEffect(() => {
|
|
314
319
|
const hasUniqueId = _get(dataWithUniqueIds, '[0].uniqueId')
|
|
315
320
|
const isEqualData = _isEqual(prevData.current, data)
|
|
@@ -414,6 +419,10 @@ export const BillingInfoContainer = React.memo(
|
|
|
414
419
|
)
|
|
415
420
|
|
|
416
421
|
try {
|
|
422
|
+
if (isWindowDefined) {
|
|
423
|
+
addAddOnsInAttributes(checkoutBody)
|
|
424
|
+
}
|
|
425
|
+
|
|
417
426
|
const res = await postOnCheckout(checkoutBody, access_token)
|
|
418
427
|
removeReferralKey()
|
|
419
428
|
onSkipBillingPage(_get(res, 'data.data.attributes'))
|
|
@@ -429,9 +438,9 @@ export const BillingInfoContainer = React.memo(
|
|
|
429
438
|
}, [skipPage, ticketsQuantity])
|
|
430
439
|
|
|
431
440
|
const collectCheckoutBody = (
|
|
432
|
-
values: Record<string,
|
|
441
|
+
values: Record<string, any>,
|
|
433
442
|
profileData?: any
|
|
434
|
-
): Record<string,
|
|
443
|
+
): Record<string, any> => {
|
|
435
444
|
let checkoutBody = {}
|
|
436
445
|
|
|
437
446
|
// Auto collect ticket holders name when it was skipped optionally
|
|
@@ -509,19 +518,24 @@ export const BillingInfoContainer = React.memo(
|
|
|
509
518
|
try {
|
|
510
519
|
if (isLoggedIn) {
|
|
511
520
|
const checkoutBody = collectCheckoutBody(values, userData)
|
|
521
|
+
|
|
522
|
+
if (isWindowDefined) {
|
|
523
|
+
addAddOnsInAttributes(checkoutBody)
|
|
524
|
+
}
|
|
525
|
+
|
|
512
526
|
const res = await postOnCheckout(checkoutBody, access_token)
|
|
513
527
|
removeReferralKey()
|
|
514
528
|
// After checkout is successful recover updated profile and store it on local storage if needed
|
|
515
529
|
if (isWindowDefined) {
|
|
516
530
|
const updatedUserData = await getProfileData(access_token)
|
|
517
531
|
const profileSpecifiedData = _get(
|
|
518
|
-
|
|
519
|
-
|
|
532
|
+
updatedUserData,
|
|
533
|
+
'data.data'
|
|
520
534
|
)
|
|
521
535
|
const profileDataObj = setLoggedUserData(profileSpecifiedData)
|
|
522
536
|
window.localStorage.setItem(
|
|
523
|
-
|
|
524
|
-
|
|
537
|
+
'user_data',
|
|
538
|
+
JSON.stringify(profileDataObj)
|
|
525
539
|
)
|
|
526
540
|
}
|
|
527
541
|
|
|
@@ -608,6 +622,11 @@ export const BillingInfoContainer = React.memo(
|
|
|
608
622
|
}
|
|
609
623
|
|
|
610
624
|
const checkoutBody = collectCheckoutBody(values, profileDataObj)
|
|
625
|
+
|
|
626
|
+
if (isWindowDefined) {
|
|
627
|
+
addAddOnsInAttributes(checkoutBody)
|
|
628
|
+
}
|
|
629
|
+
|
|
611
630
|
const res = await postOnCheckout(checkoutBody)
|
|
612
631
|
removeReferralKey()
|
|
613
632
|
handleSubmit(
|
|
@@ -731,7 +750,11 @@ export const BillingInfoContainer = React.memo(
|
|
|
731
750
|
].includes(element.name) &&
|
|
732
751
|
isLoggedIn ? null : (
|
|
733
752
|
<React.Fragment key={element.uniqueId}>
|
|
734
|
-
<div
|
|
753
|
+
<div
|
|
754
|
+
className={`${element.className} ${
|
|
755
|
+
props?.errors[element.name]
|
|
756
|
+
}`}
|
|
757
|
+
>
|
|
735
758
|
{element.component ? (
|
|
736
759
|
element.component
|
|
737
760
|
) : (
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FormControl, FormHelperText, InputLabel } from '@mui/material'
|
|
2
2
|
import Select from '@mui/material/Select'
|
|
3
|
-
import _map from 'lodash/map'
|
|
4
|
-
import _get from 'lodash/get'
|
|
5
|
-
|
|
6
|
-
import { FieldInputProps, FormikProps } from 'formik'
|
|
7
|
-
import { FormControl, InputLabel, FormHelperText } from '@mui/material'
|
|
8
3
|
import { useTheme } from '@mui/styles'
|
|
4
|
+
import { FieldInputProps, FormikProps } from 'formik'
|
|
5
|
+
import _get from 'lodash/get'
|
|
6
|
+
import _map from 'lodash/map'
|
|
7
|
+
import React from 'react'
|
|
9
8
|
|
|
10
9
|
export interface ISelectOption {
|
|
11
10
|
label: string | number;
|
|
@@ -23,6 +22,7 @@ export interface ISelectField {
|
|
|
23
22
|
// optional
|
|
24
23
|
type?: string;
|
|
25
24
|
selectOptions?: ISelectOption[];
|
|
25
|
+
onChange?: (e: any) => void;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
interface IOtherProps {
|
|
@@ -34,8 +34,9 @@ export const SelectField = ({
|
|
|
34
34
|
type = 'text',
|
|
35
35
|
field,
|
|
36
36
|
selectOptions = [] as ISelectOption[],
|
|
37
|
-
form: { touched, errors },
|
|
37
|
+
form: { touched, errors, setFieldValue },
|
|
38
38
|
theme,
|
|
39
|
+
onChange = () => {},
|
|
39
40
|
}: ISelectField & IOtherProps) => {
|
|
40
41
|
const isTouched = Boolean(_get(touched, field.name))
|
|
41
42
|
const error = _get(errors, field.name)
|
|
@@ -44,7 +45,12 @@ export const SelectField = ({
|
|
|
44
45
|
|
|
45
46
|
return (
|
|
46
47
|
<FormControl fullWidth={true}>
|
|
47
|
-
<InputLabel
|
|
48
|
+
<InputLabel
|
|
49
|
+
style={customTheme?.input}
|
|
50
|
+
htmlFor={field.name}
|
|
51
|
+
error={!!error && isTouched}
|
|
52
|
+
shrink={true}
|
|
53
|
+
>
|
|
48
54
|
{label}
|
|
49
55
|
</InputLabel>
|
|
50
56
|
<Select
|
|
@@ -61,6 +67,10 @@ export const SelectField = ({
|
|
|
61
67
|
MenuProps={{ className: theme }}
|
|
62
68
|
{...field}
|
|
63
69
|
style={customTheme?.input}
|
|
70
|
+
onChange={e => {
|
|
71
|
+
onChange(e)
|
|
72
|
+
setFieldValue(field.name, e.target.value)
|
|
73
|
+
}}
|
|
64
74
|
>
|
|
65
75
|
{_map(selectOptions, option => (
|
|
66
76
|
<option
|
package/src/components/index.ts
CHANGED
|
@@ -6,3 +6,4 @@ export { MyTicketsContainer } from './myTicketsContainer'
|
|
|
6
6
|
export { OrderDetailsContainer } from './orderDetailsContainer'
|
|
7
7
|
export { ResetPasswordContainer } from './resetPasswordContainer'
|
|
8
8
|
export { TicketResaleContainer } from './ticketResale'
|
|
9
|
+
export { AddonsContainter } from './addonsContainer'
|
|
@@ -49,6 +49,7 @@ interface OrderDetailsTypes {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const getTotal = (data: any) => {
|
|
52
|
+
if (data?.total && data?.tickets && data.tickets[0]?.currency) return data.tickets[0].currency + data.total
|
|
52
53
|
if (!data?.total || !_has(data, 'items.ticket_types.length')) return ''
|
|
53
54
|
|
|
54
55
|
return data.items.ticket_types[0].currency + data.total
|
|
@@ -234,26 +235,28 @@ export const OrderDetailsContainer = ({
|
|
|
234
235
|
</div>
|
|
235
236
|
</div>
|
|
236
237
|
</div>
|
|
237
|
-
|
|
238
|
-
<div className="link
|
|
239
|
-
<span>Personal Share Link: </span>
|
|
240
|
-
<a
|
|
241
|
-
href={data?.personal_share_link}
|
|
242
|
-
target="_blank"
|
|
243
|
-
rel="noreferrer"
|
|
244
|
-
>
|
|
245
|
-
{Boolean(personalLinkIcon) && (
|
|
246
|
-
<img src={personalLinkIcon} alt="Icon" />
|
|
247
|
-
)}
|
|
248
|
-
{data?.personal_share_link}
|
|
249
|
-
</a>
|
|
250
|
-
</div>
|
|
251
|
-
{data?.sales_referred ? (
|
|
238
|
+
{!data?.disable_referral &&
|
|
239
|
+
(<div className="personal-link">
|
|
252
240
|
<div className="link-item">
|
|
253
|
-
<
|
|
241
|
+
<span>Personal Share Link: </span>
|
|
242
|
+
<a
|
|
243
|
+
href={data?.personal_share_link}
|
|
244
|
+
target="_blank"
|
|
245
|
+
rel="noreferrer"
|
|
246
|
+
>
|
|
247
|
+
{Boolean(personalLinkIcon) && (
|
|
248
|
+
<img src={personalLinkIcon} alt="Icon"/>
|
|
249
|
+
)}
|
|
250
|
+
{data?.personal_share_link}
|
|
251
|
+
</a>
|
|
254
252
|
</div>
|
|
255
|
-
|
|
256
|
-
|
|
253
|
+
{data?.sales_referred ? (
|
|
254
|
+
<div className="link-item">
|
|
255
|
+
<p className="total-referrer">{`So far, you’ve referred ${data.sales_referred} tickets`}</p>
|
|
256
|
+
</div>
|
|
257
|
+
) : null}
|
|
258
|
+
</div>)
|
|
259
|
+
}
|
|
257
260
|
<TableContainer component={Paper}>
|
|
258
261
|
<Table className="tt-type" aria-label="collapsible table">
|
|
259
262
|
<TableHead>
|
|
@@ -26,7 +26,11 @@ import {
|
|
|
26
26
|
handleFreeSuccess,
|
|
27
27
|
handlePaymentSuccess,
|
|
28
28
|
} from '../../api'
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
createFixedFloatNormalizer,
|
|
31
|
+
currencyNormalizerCreator,
|
|
32
|
+
} from '../../normalizers'
|
|
33
|
+
import { IAddOn, IOrderData, IPaymentField } from '../../types'
|
|
30
34
|
import { CONFIGS } from '../../utils'
|
|
31
35
|
import { getQueryVariable } from '../../utils/getQueryVariable'
|
|
32
36
|
import { Loader } from '../common/index'
|
|
@@ -81,6 +85,7 @@ const initialOrderValues: IOrderData = {
|
|
|
81
85
|
price: '',
|
|
82
86
|
total: '',
|
|
83
87
|
currency: '',
|
|
88
|
+
add_ons: [] as IAddOn[],
|
|
84
89
|
}
|
|
85
90
|
const initialReviewValues = {
|
|
86
91
|
order_details: {
|
|
@@ -146,6 +151,7 @@ export const PaymentContainer = ({
|
|
|
146
151
|
price: ticket?.price,
|
|
147
152
|
total: order_details?.total,
|
|
148
153
|
currency: order_details?.currency,
|
|
154
|
+
add_ons: order_details?.add_ons || [],
|
|
149
155
|
}
|
|
150
156
|
setOrderData(orderData)
|
|
151
157
|
onGetPaymentDataSuccess(response.data)
|
|
@@ -235,11 +241,35 @@ export const PaymentContainer = ({
|
|
|
235
241
|
className = '',
|
|
236
242
|
normalizer = _identity,
|
|
237
243
|
} = field
|
|
244
|
+
const { currency } = orderData
|
|
245
|
+
const value = orderData[id as keyof IOrderData]
|
|
246
|
+
|
|
238
247
|
return (
|
|
239
248
|
<div key={id} className="order_info_block">
|
|
240
249
|
<div className="order_info_title">{label}</div>
|
|
241
250
|
<div className={`${className} order_info_text`}>
|
|
242
|
-
{
|
|
251
|
+
{typeof value === 'string'
|
|
252
|
+
? normalizer(value, currency)
|
|
253
|
+
: _map(value, item => (
|
|
254
|
+
<div key={item.id} className="add-on-container">
|
|
255
|
+
<span>{item.quantity}</span>
|
|
256
|
+
<span className="add-on-x">{' x '}</span>
|
|
257
|
+
<span>
|
|
258
|
+
{item.groupName ? item.groupName + ' - ' : ''}
|
|
259
|
+
</span>
|
|
260
|
+
<span>{item.name}</span>
|
|
261
|
+
<span>{' - '}</span>
|
|
262
|
+
<span>
|
|
263
|
+
{currencyNormalizerCreator(
|
|
264
|
+
createFixedFloatNormalizer(2)(
|
|
265
|
+
parseFloat(item.price)
|
|
266
|
+
),
|
|
267
|
+
currency
|
|
268
|
+
)}
|
|
269
|
+
</span>
|
|
270
|
+
<span className="add-on-each">{' each'}</span>
|
|
271
|
+
</div>
|
|
272
|
+
))}
|
|
243
273
|
</div>
|
|
244
274
|
</div>
|
|
245
275
|
)
|
|
@@ -9,8 +9,8 @@ interface ITicketsSectionProps {
|
|
|
9
9
|
selectedTickets: any;
|
|
10
10
|
handleTicketSelect: any;
|
|
11
11
|
sortBySoldOut: boolean;
|
|
12
|
-
ticketsHeaderComponent?: ReactNode
|
|
13
|
-
hideTicketsHeader: boolean
|
|
12
|
+
ticketsHeaderComponent?: ReactNode;
|
|
13
|
+
hideTicketsHeader: boolean;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export const TicketsSection = ({
|
|
@@ -60,7 +60,7 @@ export const TicketsSection = ({
|
|
|
60
60
|
{ticketIsDiscounted && (
|
|
61
61
|
<p className="old-price">$ {(+ticket.oldPrice).toFixed(2)}</p>
|
|
62
62
|
)}
|
|
63
|
-
<p>{ticketPrice}</p>
|
|
63
|
+
<p className={isSoldOut ? 'sold-out' : ''}>{ticketPrice}</p>
|
|
64
64
|
{!isSoldOut && !ticketIsFree && (
|
|
65
65
|
<p className="fees">
|
|
66
66
|
{ticket.feeIncluded ? '(incl. Fees)' : '(excl. Fees)'}
|
|
@@ -8,6 +8,7 @@ import axios, { AxiosError } from 'axios'
|
|
|
8
8
|
import jwt_decode from 'jwt-decode'
|
|
9
9
|
import _find from 'lodash/find'
|
|
10
10
|
import _get from 'lodash/get'
|
|
11
|
+
import _identity from 'lodash/identity'
|
|
11
12
|
import _isEmpty from 'lodash/isEmpty'
|
|
12
13
|
import _some from 'lodash/some'
|
|
13
14
|
import React, { ReactNode, useEffect, useRef, useState } from 'react'
|
|
@@ -15,6 +16,7 @@ import Button from 'react-bootstrap/Button'
|
|
|
15
16
|
|
|
16
17
|
import {
|
|
17
18
|
addToCart,
|
|
19
|
+
getCheckoutPageConfigs,
|
|
18
20
|
getEvent,
|
|
19
21
|
getTickets,
|
|
20
22
|
logout,
|
|
@@ -43,6 +45,7 @@ interface CartSuccess {
|
|
|
43
45
|
event_id: string;
|
|
44
46
|
hash?: string;
|
|
45
47
|
total?: string;
|
|
48
|
+
hasAddOn?: boolean;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
export interface IGetTickets {
|
|
@@ -96,11 +99,11 @@ export const TicketsContainer = ({
|
|
|
96
99
|
eventId,
|
|
97
100
|
onAddToCartSuccess,
|
|
98
101
|
contentStyle = {},
|
|
99
|
-
onAddToCartError =
|
|
100
|
-
onGetTicketsSuccess =
|
|
101
|
-
onGetTicketsError =
|
|
102
|
-
onLogoutSuccess =
|
|
103
|
-
onLogoutError =
|
|
102
|
+
onAddToCartError = _identity,
|
|
103
|
+
onGetTicketsSuccess = _identity,
|
|
104
|
+
onGetTicketsError = _identity,
|
|
105
|
+
onLogoutSuccess = _identity,
|
|
106
|
+
onLogoutError = _identity,
|
|
104
107
|
theme = 'light',
|
|
105
108
|
queryPromoCode = '',
|
|
106
109
|
isPromotionsEnabled = true,
|
|
@@ -216,6 +219,7 @@ export const TicketsContainer = ({
|
|
|
216
219
|
} catch (e) {
|
|
217
220
|
if (axios.isAxiosError(e)) {
|
|
218
221
|
onGetTicketsError(e)
|
|
222
|
+
setError(_get(e, 'response.data.message'))
|
|
219
223
|
}
|
|
220
224
|
} finally {
|
|
221
225
|
setIsLoading(false)
|
|
@@ -274,22 +278,24 @@ export const TicketsContainer = ({
|
|
|
274
278
|
|
|
275
279
|
try {
|
|
276
280
|
const result = await addToCart(eventId, data)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
281
|
+
const pageConfigsDataResponse = await getCheckoutPageConfigs()
|
|
282
|
+
if (result.status === 200 && pageConfigsDataResponse.status === 200) {
|
|
283
|
+
const pageConfigsData =
|
|
284
|
+
_get(pageConfigsDataResponse, 'data.attributes') || {}
|
|
285
|
+
|
|
286
|
+
const skipBillingPage = pageConfigsData.skip_billing_page ?? false
|
|
287
|
+
const nameIsRequired = pageConfigsData.names_required ?? false
|
|
288
|
+
const ageIsRequired = pageConfigsData.age_required ?? false
|
|
289
|
+
const phoneIsRequired = pageConfigsData.phone_required ?? false
|
|
290
|
+
const hasAddOn = pageConfigsData.has_add_on ?? false
|
|
286
291
|
|
|
287
292
|
let hash = ''
|
|
288
293
|
let total = ''
|
|
289
294
|
|
|
290
|
-
|
|
295
|
+
const isWindowDefined = typeof window !== 'undefined'
|
|
296
|
+
|
|
297
|
+
if (skipBillingPage && !hasAddOn) {
|
|
291
298
|
// Get user data for checkout data
|
|
292
|
-
const isWindowDefined = typeof window !== 'undefined'
|
|
293
299
|
const userData =
|
|
294
300
|
isWindowDefined && window.localStorage.getItem('user_data')
|
|
295
301
|
? JSON.parse(window.localStorage.getItem('user_data') || '')
|
|
@@ -313,6 +319,13 @@ export const TicketsContainer = ({
|
|
|
313
319
|
total = _get(checkoutResult, 'data.data.attributes.total')
|
|
314
320
|
}
|
|
315
321
|
|
|
322
|
+
if (hasAddOn && isWindowDefined) {
|
|
323
|
+
window.localStorage.setItem(
|
|
324
|
+
'tickets_quantity',
|
|
325
|
+
String(ticketQuantity)
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
316
329
|
onAddToCartSuccess({
|
|
317
330
|
skip_billing_page: skipBillingPage,
|
|
318
331
|
names_required: nameIsRequired,
|
|
@@ -321,6 +334,7 @@ export const TicketsContainer = ({
|
|
|
321
334
|
event_id: String(eventId),
|
|
322
335
|
hash,
|
|
323
336
|
total,
|
|
337
|
+
hasAddOn,
|
|
324
338
|
})
|
|
325
339
|
}
|
|
326
340
|
} catch (e) {
|
|
@@ -411,7 +425,12 @@ export const TicketsContainer = ({
|
|
|
411
425
|
{!isLoading && <ReferralLogic eventId={eventId} />}
|
|
412
426
|
<div className={`get-tickets-page ${theme}`} style={contentStyle}>
|
|
413
427
|
{error && (
|
|
414
|
-
<Alert
|
|
428
|
+
<Alert
|
|
429
|
+
severity="error"
|
|
430
|
+
onClose={onErrorClose}
|
|
431
|
+
variant="filled"
|
|
432
|
+
style={{ width: '350px' }}
|
|
433
|
+
>
|
|
415
434
|
{error}
|
|
416
435
|
</Alert>
|
|
417
436
|
)}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const getTicketSelectOptions = (
|
|
2
|
-
maxCount
|
|
3
|
-
minCount
|
|
4
|
-
multiplier
|
|
2
|
+
maxCount = 10,
|
|
3
|
+
minCount = 1,
|
|
4
|
+
multiplier = 1
|
|
5
5
|
) => {
|
|
6
6
|
const options = [{ label: 0, value: 0 }]
|
|
7
7
|
for (let i = minCount; i <= Math.min(50, maxCount); i += multiplier) {
|
package/src/index.ts
CHANGED
|
@@ -14,4 +14,5 @@ export { TicketResaleContainer } from './components'
|
|
|
14
14
|
export { RedirectModal } from './components/common/RedirectModal'
|
|
15
15
|
export { RsvpContainer } from './components/rsvpContainer'
|
|
16
16
|
export { ResetPasswordContainer } from './components/resetPasswordContainer'
|
|
17
|
-
export { ForgotPasswordModal } from './components/forgotPasswordModal'
|
|
17
|
+
export { ForgotPasswordModal } from './components/forgotPasswordModal'
|
|
18
|
+
export { AddonsContainter } from './components/addonsContainer'
|
package/src/types/index.ts
CHANGED
|
@@ -2,3 +2,5 @@ export { IBillingInfoData, IFieldData, IGroupItem } from './billing-info-data'
|
|
|
2
2
|
export { IOrderData } from './order-data'
|
|
3
3
|
export { IPaymentField } from './payment-field'
|
|
4
4
|
export { IReferralPromotion } from './referral-promotion'
|
|
5
|
+
export { ICheckoutPageConfigs } from './checkoutPageConfigs'
|
|
6
|
+
export { IAddOn } from './add_on'
|
package/src/types/order-data.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { IAddOn } from './add_on'
|
|
2
|
+
|
|
1
3
|
export interface IOrderData {
|
|
2
4
|
product_name: string;
|
|
3
5
|
ticketType: string;
|
|
4
6
|
quantity: string;
|
|
5
7
|
price: string;
|
|
6
8
|
total: string;
|
|
7
|
-
currency: string
|
|
8
|
-
|
|
9
|
+
currency: string;
|
|
10
|
+
add_ons: IAddOn[];
|
|
9
11
|
}
|
|
10
|
-
|