tf-checkout-react 1.0.43 → 1.0.47
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 +2 -0
- package/dist/components/billing-info-container/index.d.ts +22 -3
- package/dist/components/billing-info-container/utils.d.ts +2 -2
- package/dist/components/common/CheckboxField.d.ts +2 -2
- package/dist/components/confirmationContainer/index.d.ts +4 -1
- package/dist/components/loginModal/index.d.ts +8 -4
- package/dist/components/paymentContainer/index.d.ts +5 -1
- package/dist/components/registerModal/index.d.ts +3 -0
- package/dist/components/ticketsContainer/TicketRow.d.ts +10 -0
- package/dist/components/ticketsContainer/TicketsSection.d.ts +10 -0
- package/dist/components/ticketsContainer/index.d.ts +7 -1
- package/dist/components/ticketsContainer/utils.d.ts +4 -0
- package/dist/components/waitingList/index.d.ts +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/tf-checkout-react.cjs.development.css +3 -2
- package/dist/tf-checkout-react.cjs.development.js +827 -269
- 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 -272
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/billing-info-data.d.ts +2 -2
- package/dist/validators/index.d.ts +2 -1
- package/package.json +4 -1
- package/src/api/index.ts +7 -3
- package/src/components/billing-info-container/index.tsx +278 -70
- package/src/components/billing-info-container/style.css +15 -0
- package/src/components/billing-info-container/utils.ts +41 -13
- package/src/components/common/CheckboxField.tsx +3 -2
- package/src/components/common/CustomField.tsx +16 -1
- package/src/components/confirmationContainer/index.tsx +8 -3
- package/src/components/loginModal/index.tsx +46 -13
- package/src/components/paymentContainer/index.tsx +10 -0
- package/src/components/registerModal/index.tsx +20 -3
- package/src/components/ticketsContainer/TicketRow.tsx +86 -0
- package/src/components/ticketsContainer/TicketsSection.tsx +82 -0
- package/src/components/ticketsContainer/index.tsx +98 -210
- package/src/components/ticketsContainer/utils.ts +11 -0
- package/src/components/waitingList/index.tsx +162 -0
- package/src/components/waitingList/style.css +18 -0
- package/src/index.ts +2 -1
- package/src/types/billing-info-data.ts +2 -2
- package/src/validators/index.ts +9 -3
|
@@ -16,7 +16,8 @@ export interface IValues {
|
|
|
16
16
|
|
|
17
17
|
export const getInitialValues = (
|
|
18
18
|
data: any = [],
|
|
19
|
-
propsInitialValues: IValues = {}
|
|
19
|
+
propsInitialValues: IValues = {},
|
|
20
|
+
userValues: any = {}
|
|
20
21
|
): IValues => {
|
|
21
22
|
const results = _flatMapDeep(data, ({ fields }) =>
|
|
22
23
|
_map(fields, ({ groupItems }) => _map(groupItems, ({ name }) => name))
|
|
@@ -24,7 +25,7 @@ export const getInitialValues = (
|
|
|
24
25
|
|
|
25
26
|
const initialValues: IValues = {}
|
|
26
27
|
_forEach(results, item => {
|
|
27
|
-
initialValues[item] = propsInitialValues[item] || ''
|
|
28
|
+
initialValues[item] = propsInitialValues[item] || userValues[item] || ''
|
|
28
29
|
})
|
|
29
30
|
|
|
30
31
|
return initialValues
|
|
@@ -68,6 +69,13 @@ interface IUserData {
|
|
|
68
69
|
zipCode?: string;
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
interface IticketHolder {
|
|
73
|
+
first_name?: string;
|
|
74
|
+
last_name?: string;
|
|
75
|
+
phone?: string;
|
|
76
|
+
email?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
export const setLoggedUserData = (data: IUserData) => ({
|
|
72
80
|
id: data.id,
|
|
73
81
|
first_name: data.firstName,
|
|
@@ -82,34 +90,55 @@ export const setLoggedUserData = (data: IUserData) => ({
|
|
|
82
90
|
})
|
|
83
91
|
|
|
84
92
|
export const createCheckoutDataBody = (
|
|
93
|
+
ticketsQuantity: number,
|
|
85
94
|
values: IValues = {},
|
|
86
95
|
logedInValues: ILoggedInValues = {},
|
|
87
96
|
includeDob: boolean = false
|
|
88
97
|
): ICheckoutBody => {
|
|
89
98
|
const {
|
|
90
|
-
holderAge,
|
|
91
99
|
firstName,
|
|
92
100
|
lastName,
|
|
101
|
+
holderAge,
|
|
93
102
|
confirmEmail,
|
|
94
103
|
confirmPassword,
|
|
95
|
-
holderFirstName,
|
|
96
|
-
holderLastName,
|
|
97
104
|
...restValues
|
|
98
105
|
} = values
|
|
99
106
|
|
|
107
|
+
const holders = []
|
|
108
|
+
let ticket_holders: IticketHolder[] = []
|
|
109
|
+
|
|
110
|
+
for (let i = 0; i <= ticketsQuantity; i++) {
|
|
111
|
+
const individualHolder = Object.fromEntries(
|
|
112
|
+
Object.entries(values).filter(([key, _val]) => key.includes(String(i)))
|
|
113
|
+
)
|
|
114
|
+
holders.push(individualHolder)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const filteredHolders = holders.filter(
|
|
118
|
+
holder => Object.entries(holder).length > 0
|
|
119
|
+
)
|
|
120
|
+
ticket_holders = filteredHolders.map((item, index) => ({
|
|
121
|
+
first_name: item[`holderFirstName-${index}`] || '',
|
|
122
|
+
last_name: item[`holderLastName-${index}`] || '',
|
|
123
|
+
phone: item[`holderPhone-${index}`] || '',
|
|
124
|
+
email: item[`holderEmail-${index}`] || '',
|
|
125
|
+
}))
|
|
126
|
+
|
|
127
|
+
const filteredRestValue: { [key: string]: any } = {}
|
|
128
|
+
_forEach(restValues, (value, key) => {
|
|
129
|
+
if (!key.includes('holder')) {
|
|
130
|
+
filteredRestValue[key] = value
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
100
134
|
const body: ICheckoutBody = {
|
|
101
135
|
attributes: {
|
|
102
|
-
...
|
|
136
|
+
...filteredRestValue,
|
|
103
137
|
email: restValues.email || logedInValues.emailLogged,
|
|
104
138
|
confirm_email: restValues.email || logedInValues.emailLogged,
|
|
105
139
|
first_name: firstName || logedInValues.firstNameLogged,
|
|
106
140
|
last_name: lastName || logedInValues.lastNameLogged,
|
|
107
|
-
ticket_holders
|
|
108
|
-
{
|
|
109
|
-
first_name: holderFirstName,
|
|
110
|
-
last_name: holderLastName,
|
|
111
|
-
},
|
|
112
|
-
],
|
|
141
|
+
ticket_holders,
|
|
113
142
|
},
|
|
114
143
|
}
|
|
115
144
|
|
|
@@ -119,6 +148,5 @@ export const createCheckoutDataBody = (
|
|
|
119
148
|
body.attributes.dob_month = holderAgeDate.getMonth() + 1
|
|
120
149
|
body.attributes.dob_year = holderAgeDate.getFullYear()
|
|
121
150
|
}
|
|
122
|
-
|
|
123
151
|
return body
|
|
124
152
|
}
|
|
@@ -5,7 +5,7 @@ import Checkbox from '@mui/material/Checkbox'
|
|
|
5
5
|
import { FieldInputProps } from 'formik'
|
|
6
6
|
|
|
7
7
|
export interface ICheckboxField {
|
|
8
|
-
label: string;
|
|
8
|
+
label: string | number | JSX.Element;
|
|
9
9
|
field: FieldInputProps<any>;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -16,8 +16,9 @@ interface IOtherProps {
|
|
|
16
16
|
export const CheckboxField = ({
|
|
17
17
|
label,
|
|
18
18
|
field,
|
|
19
|
+
...rest
|
|
19
20
|
}: ICheckboxField & IOtherProps) => (
|
|
20
21
|
<FormGroup>
|
|
21
|
-
<FormControlLabel control={<Checkbox {...field} />} label={label} />
|
|
22
|
+
<FormControlLabel control={<Checkbox {...field} {...rest} />} label={label} />
|
|
22
23
|
</FormGroup>
|
|
23
24
|
)
|
|
@@ -4,6 +4,7 @@ import _map from 'lodash/map'
|
|
|
4
4
|
import _get from 'lodash/get'
|
|
5
5
|
|
|
6
6
|
import { FieldInputProps, FormikProps } from 'formik'
|
|
7
|
+
import { makeStyles } from '@mui/styles'
|
|
7
8
|
|
|
8
9
|
export interface ISelectOption {
|
|
9
10
|
label: string | number;
|
|
@@ -26,6 +27,14 @@ interface IOtherProps {
|
|
|
26
27
|
[key: string]: any;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
const useStyles = makeStyles({
|
|
31
|
+
input: {
|
|
32
|
+
"&::placeholder": {
|
|
33
|
+
color: "gray",
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
29
38
|
export const CustomField = ({
|
|
30
39
|
label,
|
|
31
40
|
type = 'text',
|
|
@@ -37,6 +46,7 @@ export const CustomField = ({
|
|
|
37
46
|
const isShrink = Boolean(field.value) || type === 'date' || type === 'select'
|
|
38
47
|
const isTouched = Boolean(_get(touched, field.name))
|
|
39
48
|
const error = _get(errors, field.name)
|
|
49
|
+
const classes = useStyles()
|
|
40
50
|
|
|
41
51
|
return (
|
|
42
52
|
<TextField
|
|
@@ -48,12 +58,17 @@ export const CustomField = ({
|
|
|
48
58
|
error={!!error && isTouched}
|
|
49
59
|
helperText={isTouched && error}
|
|
50
60
|
InputLabelProps={{ shrink: isShrink }}
|
|
61
|
+
InputProps={{
|
|
62
|
+
classes: {
|
|
63
|
+
input: classes.input
|
|
64
|
+
}
|
|
65
|
+
}}
|
|
51
66
|
SelectProps={{ native: true }}
|
|
52
67
|
{...field}
|
|
53
68
|
>
|
|
54
69
|
{isSelectField
|
|
55
70
|
? _map(selectOptions, option => (
|
|
56
|
-
<option key={option.value} value={option.value}>
|
|
71
|
+
<option key={option.value} value={option.value} disabled={option.disabled}>
|
|
57
72
|
{option.label}
|
|
58
73
|
</option>
|
|
59
74
|
))
|
|
@@ -7,6 +7,7 @@ import _get from 'lodash/get'
|
|
|
7
7
|
|
|
8
8
|
import { IReferralPromotion } from '../../types'
|
|
9
9
|
import { getConfirmationData } from '../../api'
|
|
10
|
+
import { AxiosError } from 'axios'
|
|
10
11
|
|
|
11
12
|
export interface IShareButton {
|
|
12
13
|
mainLabel: string;
|
|
@@ -26,6 +27,8 @@ export interface IConfirmationPage {
|
|
|
26
27
|
shareLink: string;
|
|
27
28
|
isReferralEnabled: boolean;
|
|
28
29
|
shareButtons: IShareButton[];
|
|
30
|
+
onGetConfirmationDataSuccess: (res: any) => void;
|
|
31
|
+
onGetConfirmationDataError: (e: AxiosError) => void;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
const defaultSvg =
|
|
@@ -35,6 +38,8 @@ export const ConfirmationContainer = ({
|
|
|
35
38
|
referralPromotions = [],
|
|
36
39
|
shareButtons = [],
|
|
37
40
|
shareLink = '',
|
|
41
|
+
onGetConfirmationDataSuccess = () => {},
|
|
42
|
+
onGetConfirmationDataError = () => {},
|
|
38
43
|
}: IConfirmationPage) => {
|
|
39
44
|
const inputRef = useRef(null)
|
|
40
45
|
const [data, setData] = useState<any>({})
|
|
@@ -55,14 +60,14 @@ export const ConfirmationContainer = ({
|
|
|
55
60
|
try {
|
|
56
61
|
const response = await getConfirmationData(hash)
|
|
57
62
|
const data = _get(response, 'data.data.attributes')
|
|
58
|
-
|
|
59
63
|
setData(data)
|
|
64
|
+
onGetConfirmationDataSuccess(response.data)
|
|
60
65
|
} catch (error) {
|
|
61
|
-
|
|
66
|
+
onGetConfirmationDataError(error.response)
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
})()
|
|
65
|
-
}, [])
|
|
70
|
+
}, [onGetConfirmationDataSuccess, onGetConfirmationDataError])
|
|
66
71
|
|
|
67
72
|
return (
|
|
68
73
|
<div className="confirmation-page">
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { FC, useState } from 'react'
|
|
2
|
+
import { AxiosError } from 'axios'
|
|
2
3
|
import {
|
|
3
4
|
authorize,
|
|
4
5
|
getAccessToken,
|
|
@@ -18,9 +19,12 @@ import axios from 'axios'
|
|
|
18
19
|
interface Props {
|
|
19
20
|
onClose: () => void;
|
|
20
21
|
onLogin: () => void;
|
|
21
|
-
alreadyHasUser
|
|
22
|
-
userExpired
|
|
23
|
-
|
|
22
|
+
alreadyHasUser?: boolean;
|
|
23
|
+
userExpired?: boolean;
|
|
24
|
+
onAuthorizeSuccess?: (res: any) => void;
|
|
25
|
+
onAuthorizeError?: (e: AxiosError) => void;
|
|
26
|
+
onGetProfileDataSuccess?: (res: any) => void;
|
|
27
|
+
onGetProfileDataError?: (e: AxiosError) => void;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
const style: React.CSSProperties = {
|
|
@@ -37,9 +41,12 @@ const style: React.CSSProperties = {
|
|
|
37
41
|
export const LoginModal: FC<Props> = ({
|
|
38
42
|
onClose,
|
|
39
43
|
onLogin,
|
|
40
|
-
alreadyHasUser,
|
|
41
|
-
userExpired,
|
|
42
|
-
|
|
44
|
+
alreadyHasUser = false,
|
|
45
|
+
userExpired = false,
|
|
46
|
+
onAuthorizeSuccess = () => {},
|
|
47
|
+
onAuthorizeError = () => {},
|
|
48
|
+
onGetProfileDataSuccess = () => {},
|
|
49
|
+
onGetProfileDataError = () => {},
|
|
43
50
|
}) => {
|
|
44
51
|
const [error, setError] = useState('')
|
|
45
52
|
return (
|
|
@@ -58,8 +65,8 @@ export const LoginModal: FC<Props> = ({
|
|
|
58
65
|
const bodyFormData = new FormData()
|
|
59
66
|
bodyFormData.append('email', email)
|
|
60
67
|
bodyFormData.append('password', password)
|
|
61
|
-
const resAutorize = await authorize(bodyFormData)
|
|
62
68
|
|
|
69
|
+
const resAutorize = await authorize(bodyFormData)
|
|
63
70
|
const bodyFormDataToken = new FormData()
|
|
64
71
|
bodyFormDataToken.append('code', resAutorize.data.data.code)
|
|
65
72
|
bodyFormDataToken.append('scope', 'profile')
|
|
@@ -73,22 +80,48 @@ export const LoginModal: FC<Props> = ({
|
|
|
73
80
|
ENV.CLIENT_SECRET ||
|
|
74
81
|
'b89c191eff22fdcf84ac9bfd88d005355a151ec2c83b26b9'
|
|
75
82
|
)
|
|
76
|
-
|
|
83
|
+
|
|
84
|
+
let resAccessToken = null
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
resAccessToken = await getAccessToken(bodyFormDataToken)
|
|
88
|
+
onAuthorizeSuccess(resAccessToken.data)
|
|
89
|
+
} catch (e) {
|
|
90
|
+
if (axios.isAxiosError(e)) {
|
|
91
|
+
onAuthorizeError(e)
|
|
92
|
+
}
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
77
96
|
const accessToken = _get(resAccessToken, 'data.access_token')
|
|
78
97
|
handleSetAccessToken(accessToken)
|
|
79
|
-
|
|
80
|
-
|
|
98
|
+
|
|
99
|
+
let profileResponse = null
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
profileResponse = await getProfileData(accessToken)
|
|
103
|
+
onGetProfileDataSuccess(profileResponse.data)
|
|
104
|
+
} catch (e) {
|
|
105
|
+
if (axios.isAxiosError(e)) {
|
|
106
|
+
onGetProfileDataError(e)
|
|
107
|
+
}
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const profileSpecifiedData = _get(profileResponse, 'data.data')
|
|
81
112
|
const profileDataObj = {
|
|
82
113
|
id: profileSpecifiedData.id,
|
|
83
114
|
first_name: profileSpecifiedData.firstName,
|
|
84
115
|
last_name: profileSpecifiedData.lastName,
|
|
85
|
-
email: profileSpecifiedData.email
|
|
116
|
+
email: profileSpecifiedData.email,
|
|
86
117
|
}
|
|
87
118
|
if (typeof window !== 'undefined') {
|
|
88
|
-
window.localStorage.setItem(
|
|
119
|
+
window.localStorage.setItem(
|
|
120
|
+
'user_data',
|
|
121
|
+
JSON.stringify(profileDataObj)
|
|
122
|
+
)
|
|
89
123
|
window.localStorage.setItem('access_token', accessToken)
|
|
90
124
|
}
|
|
91
|
-
setUserExpired(false)
|
|
92
125
|
onLogin()
|
|
93
126
|
} catch (e) {
|
|
94
127
|
if (axios.isAxiosError(e)) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { AxiosError } from 'axios'
|
|
2
3
|
import Container from '@mui/material/Container'
|
|
3
4
|
import Alert from '@mui/material/Alert'
|
|
4
5
|
import { Elements } from '@stripe/react-stripe-js'
|
|
@@ -26,6 +27,9 @@ export interface IPaymentPage {
|
|
|
26
27
|
formTitle?: string;
|
|
27
28
|
errorText?: string;
|
|
28
29
|
onErrorClose?: () => void;
|
|
30
|
+
onGetPaymentDataSuccess: (value: any) => void;
|
|
31
|
+
onGetPaymentDataError: (value: AxiosError) => void;
|
|
32
|
+
onPaymentError: (value: AxiosError) => void;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
const initialOrderValues: IOrderData = {
|
|
@@ -53,6 +57,9 @@ export const PaymentContainer = ({
|
|
|
53
57
|
errorText,
|
|
54
58
|
checkoutData,
|
|
55
59
|
onErrorClose = _identity,
|
|
60
|
+
onGetPaymentDataSuccess = () => {},
|
|
61
|
+
onGetPaymentDataError = () => {},
|
|
62
|
+
onPaymentError = () => {}
|
|
56
63
|
}: IPaymentPage) => {
|
|
57
64
|
const [reviewData, setReviewData] = useState(initialReviewValues)
|
|
58
65
|
const [orderData, setOrderData] = useState(initialOrderValues)
|
|
@@ -83,9 +90,11 @@ export const PaymentContainer = ({
|
|
|
83
90
|
currency: order_details?.currency,
|
|
84
91
|
}
|
|
85
92
|
setOrderData(orderData)
|
|
93
|
+
onGetPaymentDataSuccess(response.data)
|
|
86
94
|
}
|
|
87
95
|
} catch (e) {
|
|
88
96
|
setError(_get(e, 'response.data.message'))
|
|
97
|
+
onGetPaymentDataError(e.response)
|
|
89
98
|
}
|
|
90
99
|
})()
|
|
91
100
|
}, [checkoutData])
|
|
@@ -109,6 +118,7 @@ export const PaymentContainer = ({
|
|
|
109
118
|
}
|
|
110
119
|
} catch (e) {
|
|
111
120
|
setError(_get(e, 'response.data.message'))
|
|
121
|
+
onPaymentError(e.response)
|
|
112
122
|
}
|
|
113
123
|
}
|
|
114
124
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { FC } from 'react'
|
|
2
|
+
import { AxiosError } from 'axios'
|
|
2
3
|
import './style.css'
|
|
3
4
|
import { getProfileData, handleSetAccessToken, register } from '../../api'
|
|
4
5
|
import { Field, Form, Formik } from 'formik'
|
|
@@ -10,9 +11,15 @@ import { ENV } from '../../env'
|
|
|
10
11
|
interface Props {
|
|
11
12
|
onClose: () => void;
|
|
12
13
|
onRegister: () => void;
|
|
14
|
+
onGetProfileDataSuccess: (res: any) => void;
|
|
15
|
+
onGetProfileDataError: (e: AxiosError) => void;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
export const RegisterModal: FC<Props> = ({
|
|
18
|
+
export const RegisterModal: FC<Props> = ({
|
|
19
|
+
onClose,
|
|
20
|
+
onGetProfileDataSuccess = () => {},
|
|
21
|
+
onGetProfileDataError = () => {},
|
|
22
|
+
}) => (
|
|
16
23
|
<div
|
|
17
24
|
style={{
|
|
18
25
|
display: 'flex',
|
|
@@ -63,8 +70,18 @@ export const RegisterModal: FC<Props> = ({ onClose }) => (
|
|
|
63
70
|
'data.data.attributes.access_token'
|
|
64
71
|
)
|
|
65
72
|
handleSetAccessToken(access_token)
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
|
|
74
|
+
let profileResponse = null
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
profileResponse = await getProfileData(access_token)
|
|
78
|
+
onGetProfileDataSuccess(profileResponse.data)
|
|
79
|
+
} catch (e) {
|
|
80
|
+
onGetProfileDataError(e.response)
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const profileSpecifiedData = _get(profileResponse, 'data.data')
|
|
68
85
|
const profileDataObj = {
|
|
69
86
|
id: profileSpecifiedData.id,
|
|
70
87
|
first_name: profileSpecifiedData.firstName,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './style.css'
|
|
3
|
+
import _has from 'lodash/has'
|
|
4
|
+
import _get from 'lodash/get'
|
|
5
|
+
import Box from '@mui/material/Box'
|
|
6
|
+
import FormControl from '@mui/material/FormControl'
|
|
7
|
+
import MenuItem from '@mui/material/MenuItem'
|
|
8
|
+
import Select from '@mui/material/Select'
|
|
9
|
+
|
|
10
|
+
import { getTicketSelectOptions } from './utils'
|
|
11
|
+
|
|
12
|
+
interface ITicketRowProps {
|
|
13
|
+
ticketTier: any;
|
|
14
|
+
prevTicketTier: any;
|
|
15
|
+
selectedTickets: any;
|
|
16
|
+
handleTicketSelect: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const TicketRow = ({
|
|
20
|
+
ticketTier,
|
|
21
|
+
prevTicketTier,
|
|
22
|
+
selectedTickets,
|
|
23
|
+
handleTicketSelect,
|
|
24
|
+
}: ITicketRowProps) => {
|
|
25
|
+
const soldOutMessage = ticketTier.soldOutMessage
|
|
26
|
+
? `${ticketTier.soldOutMessage}`.toUpperCase()
|
|
27
|
+
: 'SOLD OUT'
|
|
28
|
+
const isSalesClosed =
|
|
29
|
+
!ticketTier.salesStarted ||
|
|
30
|
+
ticketTier.salesEnded ||
|
|
31
|
+
!_has(ticketTier, 'maxQuantity') ||
|
|
32
|
+
ticketTier.maxQuantity === 0
|
|
33
|
+
const options = getTicketSelectOptions(
|
|
34
|
+
ticketTier.maxQuantity,
|
|
35
|
+
ticketTier.minQuantity,
|
|
36
|
+
ticketTier.multiplier
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
const onSaleContent = isSalesClosed ? null : (
|
|
40
|
+
<div className="get-tickets">
|
|
41
|
+
<Box className="get-tickets__selectbox">
|
|
42
|
+
<FormControl fullWidth>
|
|
43
|
+
<Select
|
|
44
|
+
value={
|
|
45
|
+
selectedTickets[ticketTier.id]
|
|
46
|
+
? selectedTickets[ticketTier.id]
|
|
47
|
+
: 0
|
|
48
|
+
}
|
|
49
|
+
onChange={handleTicketSelect}
|
|
50
|
+
displayEmpty
|
|
51
|
+
inputProps={{ 'aria-label': 'Without label' }}
|
|
52
|
+
MenuProps={{
|
|
53
|
+
PaperProps: {
|
|
54
|
+
sx: { maxHeight: 150 },
|
|
55
|
+
className: 'get-tickets-paper',
|
|
56
|
+
},
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{options.map((option, index) => (
|
|
60
|
+
<MenuItem key={index} value={option.value}>
|
|
61
|
+
{option.value}
|
|
62
|
+
</MenuItem>
|
|
63
|
+
))}
|
|
64
|
+
</Select>
|
|
65
|
+
</FormControl>
|
|
66
|
+
</Box>
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
let returnValue: any = ''
|
|
71
|
+
|
|
72
|
+
if (ticketTier.sold_out || !ticketTier.displayTicket || ticketTier.soldOut) {
|
|
73
|
+
returnValue = soldOutMessage
|
|
74
|
+
}
|
|
75
|
+
if (!+ticketTier.cost && !+ticketTier.price) {
|
|
76
|
+
returnValue = 'FREE'
|
|
77
|
+
}
|
|
78
|
+
if (ticketTier.displayTicket) {
|
|
79
|
+
returnValue = onSaleContent
|
|
80
|
+
}
|
|
81
|
+
if (_get(prevTicketTier, 'in_stock')) {
|
|
82
|
+
returnValue = 'SOON'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return <>{returnValue} </>
|
|
86
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './style.css'
|
|
3
|
+
|
|
4
|
+
import _sortBy from 'lodash/sortBy'
|
|
5
|
+
import { TicketRow } from './TicketRow'
|
|
6
|
+
|
|
7
|
+
interface ITicketsSectionProps {
|
|
8
|
+
ticketsList: any;
|
|
9
|
+
selectedTickets: any;
|
|
10
|
+
handleTicketSelect: any;
|
|
11
|
+
promoCodeIsApplied: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const TicketsSection = ({
|
|
15
|
+
ticketsList,
|
|
16
|
+
selectedTickets,
|
|
17
|
+
handleTicketSelect,
|
|
18
|
+
promoCodeIsApplied,
|
|
19
|
+
}: ITicketsSectionProps) => {
|
|
20
|
+
const sortedTicketsList = _sortBy(ticketsList, 'sortOrder')
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
{sortedTicketsList.map((ticket, i, arr) => {
|
|
24
|
+
const isSoldOut =
|
|
25
|
+
ticket.sold_out || !ticket.displayTicket || ticket.soldOut
|
|
26
|
+
const ticketSelect = (event: any) => {
|
|
27
|
+
const { value } = event.target
|
|
28
|
+
handleTicketSelect(ticket.id, value)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let ticketIsDiscounted = false
|
|
32
|
+
if (
|
|
33
|
+
ticket.oldPrice &&
|
|
34
|
+
promoCodeIsApplied &&
|
|
35
|
+
!isSoldOut &&
|
|
36
|
+
ticket.oldPrice !== ticket.price
|
|
37
|
+
) {
|
|
38
|
+
ticketIsDiscounted = true
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
key={ticket.id || ticket.name}
|
|
44
|
+
className={`event-detail__tier ${isSoldOut ? 'disabled' : ''}`}
|
|
45
|
+
>
|
|
46
|
+
<div className="event-detail__tier-name">
|
|
47
|
+
{ticket.displayName || ticket.name}
|
|
48
|
+
</div>
|
|
49
|
+
<div className="event-tickets-container">
|
|
50
|
+
<div className="event-detail__tier-price">
|
|
51
|
+
{ticketIsDiscounted && (
|
|
52
|
+
<p className="old-price">$ {(+ticket.oldPrice).toFixed(2)}</p>
|
|
53
|
+
)}
|
|
54
|
+
<p>
|
|
55
|
+
{isSoldOut
|
|
56
|
+
? 'SOLD OUT'
|
|
57
|
+
: `$ ${(+ticket.cost || +ticket.price).toFixed(2)}`}
|
|
58
|
+
</p>
|
|
59
|
+
{!isSoldOut && (
|
|
60
|
+
<p className="fees">
|
|
61
|
+
{ticket.taxesIncluded ? '(incl. Fees)' : '(excl. Fees)'}
|
|
62
|
+
</p>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
<div
|
|
66
|
+
className="event-detail__tier-state"
|
|
67
|
+
style={{ minWidth: 55 }}
|
|
68
|
+
>
|
|
69
|
+
<TicketRow
|
|
70
|
+
ticketTier={ticket}
|
|
71
|
+
prevTicketTier={arr[i - 1]}
|
|
72
|
+
selectedTickets={selectedTickets}
|
|
73
|
+
handleTicketSelect={ticketSelect}
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
)
|
|
79
|
+
})}
|
|
80
|
+
</>
|
|
81
|
+
)
|
|
82
|
+
}
|