tf-checkout-react 1.0.50 → 1.0.54
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/billing-info-container/utils.d.ts +11 -1
- package/dist/components/common/CustomField.d.ts +1 -1
- package/dist/components/common/SelectField.d.ts +20 -0
- package/dist/tf-checkout-react.cjs.development.css +3 -3
- package/dist/tf-checkout-react.cjs.development.js +274 -82
- 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 +282 -90
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/billing-info-data.d.ts +3 -0
- package/dist/utils/formikErrorFocus.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/billing-info-container/index.tsx +61 -29
- package/src/components/billing-info-container/style.css +4 -0
- package/src/components/billing-info-container/utils.ts +53 -1
- package/src/components/common/CustomField.tsx +5 -4
- package/src/components/common/SelectField.tsx +89 -0
- package/src/components/paymentContainer/index.tsx +1 -0
- package/src/components/paymentContainer/style.css +0 -8
- package/src/components/stripePayment/index.tsx +31 -3
- package/src/components/stripePayment/style.css +4 -0
- package/src/components/waitingList/index.tsx +2 -1
- package/src/types/billing-info-data.ts +3 -0
- package/src/utils/formikErrorFocus.ts +24 -0
|
@@ -7,6 +7,7 @@ export interface IGroupItem {
|
|
|
7
7
|
required?: boolean;
|
|
8
8
|
component?: ReactNode | JSX.Element | HTMLElement;
|
|
9
9
|
onValidate?: (value: any) => void;
|
|
10
|
+
uniqueId?: string;
|
|
10
11
|
[key: string]: any;
|
|
11
12
|
}
|
|
12
13
|
export interface IFieldData {
|
|
@@ -15,10 +16,12 @@ export interface IFieldData {
|
|
|
15
16
|
groupLabel?: string | JSX.Element | HTMLElement;
|
|
16
17
|
groupLabelClassName?: string;
|
|
17
18
|
id: number;
|
|
19
|
+
uniqueId?: string;
|
|
18
20
|
}
|
|
19
21
|
export interface IBillingInfoData {
|
|
20
22
|
id: number | string;
|
|
21
23
|
fields: IFieldData[];
|
|
22
24
|
label?: string | JSX.Element;
|
|
23
25
|
labelClassName?: string;
|
|
26
|
+
uniqueId?: string;
|
|
24
27
|
}
|
package/package.json
CHANGED
|
@@ -30,9 +30,11 @@ import {
|
|
|
30
30
|
import { LoginModal } from '../loginModal'
|
|
31
31
|
import { RegisterModal } from '../registerModal'
|
|
32
32
|
import {
|
|
33
|
+
assingUniqueIds,
|
|
33
34
|
createCheckoutDataBody,
|
|
34
35
|
createRegisterFormData,
|
|
35
36
|
getInitialValues,
|
|
37
|
+
getValidateFunctions,
|
|
36
38
|
setLoggedUserData,
|
|
37
39
|
} from './utils'
|
|
38
40
|
import { CustomField } from '../common/CustomField'
|
|
@@ -41,10 +43,11 @@ import { CheckboxField } from '../common/CheckboxField'
|
|
|
41
43
|
import { CircularProgress } from '@mui/material'
|
|
42
44
|
import { nanoid } from 'nanoid'
|
|
43
45
|
import { getQueryVariable } from '../../utils/getQueryVariable'
|
|
46
|
+
import { SelectField } from '../common/SelectField'
|
|
47
|
+
import { ErrorFocus } from '../../utils/formikErrorFocus'
|
|
44
48
|
|
|
45
49
|
// const TTFLOGO = require('./logo-ttf.png')
|
|
46
50
|
|
|
47
|
-
|
|
48
51
|
export interface IBillingInfoPage {
|
|
49
52
|
data?: IBillingInfoData[];
|
|
50
53
|
ticketHoldersFields?: IBillingInfoData;
|
|
@@ -52,7 +55,7 @@ export interface IBillingInfoPage {
|
|
|
52
55
|
values: FormikValues,
|
|
53
56
|
formikHelpers: FormikHelpers<FormikValues>,
|
|
54
57
|
eventId: any,
|
|
55
|
-
res: any
|
|
58
|
+
res: any
|
|
56
59
|
) => void;
|
|
57
60
|
|
|
58
61
|
onRegisterSuccess?: (value: {
|
|
@@ -143,7 +146,7 @@ const LogicRunner: FC<{
|
|
|
143
146
|
lastName: parsedData?.last_name,
|
|
144
147
|
email: parsedData?.email || '',
|
|
145
148
|
phone: parsedData?.phone || '',
|
|
146
|
-
confirmEmail: '',
|
|
149
|
+
confirmEmail: parsedData?.email || '',
|
|
147
150
|
state: parsedData?.state || '',
|
|
148
151
|
street_address: parsedData?.street_address || '',
|
|
149
152
|
country: parsedData?.country || 1,
|
|
@@ -199,6 +202,10 @@ export const BillingInfoContainer = ({
|
|
|
199
202
|
? window.localStorage.getItem('access_token') || ''
|
|
200
203
|
: ''
|
|
201
204
|
|
|
205
|
+
const [dataWithUniqueIds, setDataWithUniqueIds] = useState<
|
|
206
|
+
IBillingInfoData[]
|
|
207
|
+
>(data)
|
|
208
|
+
|
|
202
209
|
const [cartInfoData, setCartInfo] = useState<any>({})
|
|
203
210
|
const [countries, setCountries] = useState<any>([])
|
|
204
211
|
const [states, setStates] = useState<any>([])
|
|
@@ -234,7 +241,11 @@ export const BillingInfoContainer = ({
|
|
|
234
241
|
const showTicketHolderName = getQueryVariable('names_required') === 'true'
|
|
235
242
|
const eventId = getQueryVariable('event_id')
|
|
236
243
|
const optedInFieldValue: boolean = _get(cartInfoData, 'optedIn', false)
|
|
237
|
-
const
|
|
244
|
+
const hideTtfOptIn: boolean = _get(cartInfoData, 'hide_ttf_opt_in', true)
|
|
245
|
+
|
|
246
|
+
if (!_get(dataWithUniqueIds, '[0].uniqueId')) {
|
|
247
|
+
setDataWithUniqueIds(assingUniqueIds(data))
|
|
248
|
+
}
|
|
238
249
|
|
|
239
250
|
const getQuantity = (cart: any = []) => {
|
|
240
251
|
let qty: any = 0
|
|
@@ -276,7 +287,9 @@ export const BillingInfoContainer = ({
|
|
|
276
287
|
const cartInfo = _get(res, 'data.data.attributes')
|
|
277
288
|
setCartInfo(cartInfo)
|
|
278
289
|
const { cart = [] } = cartInfo
|
|
279
|
-
setTicketsQuantity(
|
|
290
|
+
setTicketsQuantity(
|
|
291
|
+
new Array(getQuantity(cart)).fill(null).map(() => nanoid())
|
|
292
|
+
)
|
|
280
293
|
onGetCartSuccess(res.data)
|
|
281
294
|
} catch (e) {
|
|
282
295
|
if (axios.isAxiosError(e)) {
|
|
@@ -314,7 +327,7 @@ export const BillingInfoContainer = ({
|
|
|
314
327
|
<>
|
|
315
328
|
<Formik
|
|
316
329
|
initialValues={getInitialValues(
|
|
317
|
-
|
|
330
|
+
dataWithUniqueIds,
|
|
318
331
|
{
|
|
319
332
|
...initialValues,
|
|
320
333
|
email: emailLogged,
|
|
@@ -347,11 +360,22 @@ export const BillingInfoContainer = ({
|
|
|
347
360
|
showDOB
|
|
348
361
|
)
|
|
349
362
|
const res = await postOnCheckout(checkoutBody, access_token)
|
|
350
|
-
handleSubmit(
|
|
363
|
+
handleSubmit(
|
|
364
|
+
values,
|
|
365
|
+
formikHelpers as FormikHelpers<any>,
|
|
366
|
+
eventId,
|
|
367
|
+
res
|
|
368
|
+
)
|
|
351
369
|
return
|
|
352
370
|
}
|
|
353
371
|
|
|
354
|
-
const
|
|
372
|
+
const checkoutBodyForRegistration = createCheckoutDataBody(
|
|
373
|
+
ticketsQuantity.length,
|
|
374
|
+
values,
|
|
375
|
+
{ emailLogged, firstNameLogged, lastNameLogged },
|
|
376
|
+
showDOB
|
|
377
|
+
)
|
|
378
|
+
const bodyFormData = createRegisterFormData(values, checkoutBodyForRegistration)
|
|
355
379
|
|
|
356
380
|
let access_token_register = null
|
|
357
381
|
try {
|
|
@@ -416,7 +440,12 @@ export const BillingInfoContainer = ({
|
|
|
416
440
|
checkoutBody,
|
|
417
441
|
access_token_register
|
|
418
442
|
)
|
|
419
|
-
handleSubmit(
|
|
443
|
+
handleSubmit(
|
|
444
|
+
values,
|
|
445
|
+
formikHelpers as FormikHelpers<any>,
|
|
446
|
+
eventId,
|
|
447
|
+
res
|
|
448
|
+
)
|
|
420
449
|
} catch (e) {
|
|
421
450
|
if (axios.isAxiosError(e)) {
|
|
422
451
|
if (e.response?.data.error === 'invalid_token') {
|
|
@@ -434,6 +463,7 @@ export const BillingInfoContainer = ({
|
|
|
434
463
|
>
|
|
435
464
|
{(props: FormikProps<any>) => (
|
|
436
465
|
<Form onSubmit={props.handleSubmit}>
|
|
466
|
+
<ErrorFocus />
|
|
437
467
|
<LogicRunner
|
|
438
468
|
values={props.values}
|
|
439
469
|
setStates={setStates}
|
|
@@ -467,7 +497,7 @@ export const BillingInfoContainer = ({
|
|
|
467
497
|
</div>
|
|
468
498
|
</div>
|
|
469
499
|
)}
|
|
470
|
-
{_map(
|
|
500
|
+
{_map(dataWithUniqueIds, item => {
|
|
471
501
|
const { label, labelClassName, fields } = item
|
|
472
502
|
if (
|
|
473
503
|
label === 'Ticket Holders' &&
|
|
@@ -477,7 +507,7 @@ export const BillingInfoContainer = ({
|
|
|
477
507
|
return null
|
|
478
508
|
}
|
|
479
509
|
return (
|
|
480
|
-
<React.Fragment key={
|
|
510
|
+
<React.Fragment key={item.uniqueId}>
|
|
481
511
|
<p className={labelClassName}>{label}</p>
|
|
482
512
|
{_map(fields, group => {
|
|
483
513
|
const {
|
|
@@ -487,7 +517,7 @@ export const BillingInfoContainer = ({
|
|
|
487
517
|
groupLabelClassName,
|
|
488
518
|
} = group
|
|
489
519
|
return (
|
|
490
|
-
<React.Fragment key={
|
|
520
|
+
<React.Fragment key={group.uniqueId}>
|
|
491
521
|
{!isLoggedIn && (
|
|
492
522
|
<div className={groupLabelClassName}>
|
|
493
523
|
{groupLabel}
|
|
@@ -511,7 +541,7 @@ export const BillingInfoContainer = ({
|
|
|
511
541
|
if (el.name === 'holderAge' && !showDOB) {
|
|
512
542
|
return false
|
|
513
543
|
}
|
|
514
|
-
if (el.name === '
|
|
544
|
+
if (el.name === 'ttf_opt_in' && hideTtfOptIn) {
|
|
515
545
|
return false
|
|
516
546
|
}
|
|
517
547
|
return true
|
|
@@ -520,10 +550,14 @@ export const BillingInfoContainer = ({
|
|
|
520
550
|
['password', 'confirmPassword'].includes(
|
|
521
551
|
element.name
|
|
522
552
|
) && isLoggedIn ? null : (
|
|
523
|
-
<React.Fragment key={
|
|
524
|
-
{element.name === 'email'
|
|
525
|
-
|
|
526
|
-
|
|
553
|
+
<React.Fragment key={element.uniqueId}>
|
|
554
|
+
{element.name === 'email' ? (
|
|
555
|
+
<div className="email-checking">
|
|
556
|
+
{`IMPORTANT: Please double check that your
|
|
557
|
+
email address is correct. It's where we
|
|
558
|
+
send your confirmation and e-tickets to!`}
|
|
559
|
+
</div>
|
|
560
|
+
) : null}
|
|
527
561
|
<div className={element.className}>
|
|
528
562
|
{element.component ? (
|
|
529
563
|
element.component
|
|
@@ -532,17 +566,15 @@ export const BillingInfoContainer = ({
|
|
|
532
566
|
name={element.name}
|
|
533
567
|
label={element.label}
|
|
534
568
|
type={element.type}
|
|
535
|
-
validate={
|
|
536
|
-
element
|
|
537
|
-
|
|
538
|
-
: () => {},
|
|
539
|
-
element.onValidate
|
|
540
|
-
? element.onValidate
|
|
541
|
-
: () => {}
|
|
569
|
+
validate={getValidateFunctions(
|
|
570
|
+
element,
|
|
571
|
+
states
|
|
542
572
|
)}
|
|
543
573
|
component={
|
|
544
574
|
element.type === 'checkbox'
|
|
545
575
|
? CheckboxField
|
|
576
|
+
: element.type === 'select'
|
|
577
|
+
? SelectField
|
|
546
578
|
: CustomField
|
|
547
579
|
}
|
|
548
580
|
selectOptions={
|
|
@@ -567,21 +599,21 @@ export const BillingInfoContainer = ({
|
|
|
567
599
|
)
|
|
568
600
|
})}
|
|
569
601
|
{showTicketHolderName && (
|
|
570
|
-
<React.Fragment
|
|
602
|
+
<React.Fragment>
|
|
571
603
|
<div className="ticket-holders-fields">
|
|
572
604
|
<p>{ticketHoldersFields.label}</p>
|
|
573
605
|
{_map(ticketsQuantity, (_item, index) => (
|
|
574
|
-
<div key={
|
|
606
|
+
<div key={_item}>
|
|
575
607
|
<h5>Ticket {index + 1}</h5>
|
|
576
608
|
{_map(ticketHoldersFields.fields, group => {
|
|
577
609
|
const { groupClassname, groupItems } = group
|
|
578
610
|
return (
|
|
579
|
-
<div key={
|
|
611
|
+
<div key={group.uniqueId}>
|
|
580
612
|
<div className={groupClassname}>
|
|
581
613
|
{_map(groupItems, element => (
|
|
582
614
|
<div
|
|
583
615
|
className={element.className}
|
|
584
|
-
key={
|
|
616
|
+
key={element.uniqueId}
|
|
585
617
|
>
|
|
586
618
|
<Field
|
|
587
619
|
name={`${element.name}-${index}`}
|
|
@@ -613,7 +645,7 @@ export const BillingInfoContainer = ({
|
|
|
613
645
|
</div>
|
|
614
646
|
</React.Fragment>
|
|
615
647
|
)}
|
|
616
|
-
<div className="button-container"
|
|
648
|
+
<div className="button-container">
|
|
617
649
|
<LoadingButton
|
|
618
650
|
type="submit"
|
|
619
651
|
variant="contained"
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import _map from 'lodash/map'
|
|
2
|
+
import _get from 'lodash/get'
|
|
2
3
|
import _forEach from 'lodash/forEach'
|
|
3
4
|
import _flatMapDeep from 'lodash/flatMapDeep'
|
|
5
|
+
import _isArray from 'lodash/isArray'
|
|
4
6
|
|
|
5
7
|
import { ENV } from '../../env'
|
|
8
|
+
import { IGroupItem } from '../../types'
|
|
9
|
+
import { combineValidators, requiredValidator } from '../../validators'
|
|
10
|
+
import { nanoid } from 'nanoid'
|
|
6
11
|
|
|
7
12
|
export interface ILoggedInValues {
|
|
8
13
|
emailLogged?: string;
|
|
@@ -31,7 +36,10 @@ export const getInitialValues = (
|
|
|
31
36
|
return initialValues
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
export const createRegisterFormData = (
|
|
39
|
+
export const createRegisterFormData = (
|
|
40
|
+
values: IValues = {},
|
|
41
|
+
checkoutBody: { attributes: { [key: string]: any } }
|
|
42
|
+
): FormData => {
|
|
35
43
|
const bodyFormData = new FormData()
|
|
36
44
|
bodyFormData.append('first_name', values.firstName)
|
|
37
45
|
bodyFormData.append('last_name', values.lastName)
|
|
@@ -46,6 +54,11 @@ export const createRegisterFormData = (values: IValues = {}): FormData => {
|
|
|
46
54
|
'client_secret',
|
|
47
55
|
ENV.CLIENT_SECRET || 'b89c191eff22fdcf84ac9bfd88d005355a151ec2c83b26b9'
|
|
48
56
|
)
|
|
57
|
+
|
|
58
|
+
_forEach(checkoutBody.attributes, (item: any, key: string) => {
|
|
59
|
+
bodyFormData.append(key, item)
|
|
60
|
+
})
|
|
61
|
+
|
|
49
62
|
return bodyFormData
|
|
50
63
|
}
|
|
51
64
|
|
|
@@ -81,6 +94,7 @@ export const setLoggedUserData = (data: IUserData) => ({
|
|
|
81
94
|
first_name: data.firstName,
|
|
82
95
|
last_name: data.lastName,
|
|
83
96
|
email: data.email,
|
|
97
|
+
confirmEmail: data.email,
|
|
84
98
|
city: data?.city || '',
|
|
85
99
|
country: data?.country || '',
|
|
86
100
|
phone: data?.phone || '',
|
|
@@ -150,3 +164,41 @@ export const createCheckoutDataBody = (
|
|
|
150
164
|
}
|
|
151
165
|
return body
|
|
152
166
|
}
|
|
167
|
+
|
|
168
|
+
export const getValidateFunctions = (
|
|
169
|
+
element: IGroupItem,
|
|
170
|
+
states: Array<{ [key: string]: any }>
|
|
171
|
+
) => {
|
|
172
|
+
const validationFunctions: any[] = []
|
|
173
|
+
|
|
174
|
+
if (element.required) {
|
|
175
|
+
if (
|
|
176
|
+
element.name !== 'state' ||
|
|
177
|
+
(element.name === 'state' && states.length)
|
|
178
|
+
) {
|
|
179
|
+
validationFunctions.push(requiredValidator)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (element.onValidate) {
|
|
184
|
+
validationFunctions.push(element.onValidate)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return combineValidators(...validationFunctions)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const assingUniqueIds = (data: any): any => {
|
|
191
|
+
if (_get(data[0], 'uniqueId')) {
|
|
192
|
+
return data
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return _map(data, (item: any) => {
|
|
196
|
+
_forEach(item, (itemValue: string, key) => {
|
|
197
|
+
if (_isArray(itemValue)) {
|
|
198
|
+
item[key] = assingUniqueIds(itemValue)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
return { ...item, uniqueId: nanoid() }
|
|
203
|
+
})
|
|
204
|
+
}
|
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import TextField from '@mui/material/TextField'
|
|
3
3
|
import _map from 'lodash/map'
|
|
4
4
|
import _get from 'lodash/get'
|
|
5
|
+
import _includes from 'lodash/includes'
|
|
5
6
|
|
|
6
7
|
import { FieldInputProps, FormikProps } from 'formik'
|
|
7
8
|
import { makeStyles } from '@mui/styles'
|
|
@@ -41,13 +42,14 @@ export const CustomField = ({
|
|
|
41
42
|
type = 'text',
|
|
42
43
|
field,
|
|
43
44
|
selectOptions = [] as ISelectOption[],
|
|
44
|
-
form: { touched, errors },
|
|
45
|
+
form: { touched, errors, submitCount },
|
|
45
46
|
theme,
|
|
46
47
|
}: ICustomField & IOtherProps) => {
|
|
47
48
|
const isSelectField = type === 'select'
|
|
48
|
-
const isShrink = Boolean(field.value) || type === 'date' || type === 'select'
|
|
49
|
-
const isTouched = Boolean(_get(touched, field.name))
|
|
50
49
|
const error = _get(errors, field.name)
|
|
50
|
+
const isTouched =
|
|
51
|
+
Boolean(_get(touched, field.name)) ||
|
|
52
|
+
(_includes(field.name, 'holder') && !!error && !!submitCount)
|
|
51
53
|
const classes = useStyles()
|
|
52
54
|
|
|
53
55
|
return (
|
|
@@ -59,7 +61,6 @@ export const CustomField = ({
|
|
|
59
61
|
fullWidth={true}
|
|
60
62
|
error={!!error && isTouched}
|
|
61
63
|
helperText={isTouched && error}
|
|
62
|
-
InputLabelProps={{ shrink: isShrink }}
|
|
63
64
|
InputProps={{
|
|
64
65
|
classes: {
|
|
65
66
|
input: classes.input,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from 'react'
|
|
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 { makeStyles } from '@mui/styles'
|
|
8
|
+
import { FormControl, InputLabel, FormHelperText } from '@mui/material'
|
|
9
|
+
|
|
10
|
+
export interface ISelectOption {
|
|
11
|
+
label: string | number;
|
|
12
|
+
value?: string | number;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ISelectField {
|
|
17
|
+
label: string;
|
|
18
|
+
|
|
19
|
+
field: FieldInputProps<any>;
|
|
20
|
+
form: FormikProps<any>;
|
|
21
|
+
theme: 'dark' | 'light';
|
|
22
|
+
|
|
23
|
+
// optional
|
|
24
|
+
type?: string;
|
|
25
|
+
selectOptions?: ISelectOption[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface IOtherProps {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const useStyles = makeStyles({
|
|
33
|
+
input: {
|
|
34
|
+
'&::placeholder': {
|
|
35
|
+
color: 'gray',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export const SelectField = ({
|
|
41
|
+
label,
|
|
42
|
+
type = 'text',
|
|
43
|
+
field,
|
|
44
|
+
selectOptions = [] as ISelectOption[],
|
|
45
|
+
form: { touched, errors },
|
|
46
|
+
theme,
|
|
47
|
+
}: ISelectField & IOtherProps) => {
|
|
48
|
+
const isTouched = Boolean(_get(touched, field.name))
|
|
49
|
+
const error = _get(errors, field.name)
|
|
50
|
+
const classes = useStyles()
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<FormControl fullWidth={true}>
|
|
54
|
+
<InputLabel htmlFor={field.name} error={!!error && isTouched} shrink={true}>
|
|
55
|
+
{label}
|
|
56
|
+
</InputLabel>
|
|
57
|
+
<Select
|
|
58
|
+
id={field.name}
|
|
59
|
+
label={label}
|
|
60
|
+
type={type}
|
|
61
|
+
fullWidth={true}
|
|
62
|
+
error={!!error && isTouched}
|
|
63
|
+
inputProps={{
|
|
64
|
+
id: field.name,
|
|
65
|
+
classes: {
|
|
66
|
+
input: classes.input,
|
|
67
|
+
},
|
|
68
|
+
}}
|
|
69
|
+
native={true}
|
|
70
|
+
className={theme}
|
|
71
|
+
MenuProps={{ className: theme }}
|
|
72
|
+
{...field}
|
|
73
|
+
>
|
|
74
|
+
{_map(selectOptions, option => (
|
|
75
|
+
<option
|
|
76
|
+
key={option.value}
|
|
77
|
+
value={option.value}
|
|
78
|
+
disabled={option.disabled}
|
|
79
|
+
>
|
|
80
|
+
{option.label}
|
|
81
|
+
</option>
|
|
82
|
+
))}
|
|
83
|
+
</Select>
|
|
84
|
+
{isTouched && error ? (
|
|
85
|
+
<FormHelperText error={!!error && isTouched}>{error}</FormHelperText>
|
|
86
|
+
) : null}
|
|
87
|
+
</FormControl>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -37,7 +37,6 @@ export interface ICheckoutForm {
|
|
|
37
37
|
total: string;
|
|
38
38
|
currency: string;
|
|
39
39
|
onSubmit: (error: any) => Promise<any>;
|
|
40
|
-
|
|
41
40
|
error?: string | null;
|
|
42
41
|
stripeCardOptions?: StripeCardNumberElementOptions;
|
|
43
42
|
stripe_client_secret: string;
|
|
@@ -70,6 +69,8 @@ const CheckoutForm = ({
|
|
|
70
69
|
const elements = useElements()
|
|
71
70
|
const [postalCode, setPostalCode] = useState<any>('')
|
|
72
71
|
const [stripeError, setStripeError] = useState<any>(null)
|
|
72
|
+
const [checkboxes, setCheckboxes] = useState<any>([])
|
|
73
|
+
const [allowSubmit, setAllowSubmit] = useState(false)
|
|
73
74
|
|
|
74
75
|
const handleSubmit = async (event: React.SyntheticEvent) => {
|
|
75
76
|
handleSetLoading(true)
|
|
@@ -127,6 +128,18 @@ const CheckoutForm = ({
|
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
const handleCheckboxes=(e:any) => {
|
|
132
|
+
const checkbox = e.target
|
|
133
|
+
const updatedCheckedState = checkboxes.map((item:any) =>{
|
|
134
|
+
const value = item.id === checkbox.name ? !item.checked : item.checked
|
|
135
|
+
return {
|
|
136
|
+
...item,
|
|
137
|
+
checked: value
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
setCheckboxes(updatedCheckedState)
|
|
141
|
+
}
|
|
142
|
+
|
|
130
143
|
const onChangePostalCode = (e: any) => {
|
|
131
144
|
setPostalCode(e.target.value)
|
|
132
145
|
}
|
|
@@ -141,6 +154,19 @@ const CheckoutForm = ({
|
|
|
141
154
|
}
|
|
142
155
|
}, [])
|
|
143
156
|
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if(conditions.length){
|
|
159
|
+
setCheckboxes(conditions)
|
|
160
|
+
}
|
|
161
|
+
}, [conditions])
|
|
162
|
+
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if(checkboxes.length){
|
|
165
|
+
const allChecked = checkboxes.every((item:any)=>item?.checked === true)
|
|
166
|
+
setAllowSubmit(allChecked)
|
|
167
|
+
}
|
|
168
|
+
}, [checkboxes])
|
|
169
|
+
|
|
144
170
|
const buttonIsDiabled = !stripe || !!error || isLoading
|
|
145
171
|
|
|
146
172
|
return (
|
|
@@ -178,7 +204,7 @@ const CheckoutForm = ({
|
|
|
178
204
|
/>
|
|
179
205
|
</label>
|
|
180
206
|
</div>
|
|
181
|
-
{
|
|
207
|
+
{checkboxes?.map((checkbox: any) => (
|
|
182
208
|
<div
|
|
183
209
|
className={'billing-info-container__singleField'}
|
|
184
210
|
key={checkbox.id}
|
|
@@ -188,13 +214,15 @@ const CheckoutForm = ({
|
|
|
188
214
|
name={checkbox.id}
|
|
189
215
|
label={checkbox.text}
|
|
190
216
|
required={true}
|
|
217
|
+
onChange={handleCheckboxes}
|
|
218
|
+
checked={checkbox.checked}
|
|
191
219
|
/>
|
|
192
220
|
</div>
|
|
193
221
|
</div>
|
|
194
222
|
))}
|
|
195
223
|
<div
|
|
196
224
|
className={`payment_button ${
|
|
197
|
-
buttonIsDiabled ? 'disabled-payment-button' : ''
|
|
225
|
+
buttonIsDiabled || !allowSubmit ? 'disabled-payment-button' : ''
|
|
198
226
|
}`}
|
|
199
227
|
>
|
|
200
228
|
<button disabled={buttonIsDiabled} type="submit">
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
emailValidator,
|
|
11
11
|
} from '../../validators'
|
|
12
12
|
import { ENV } from '../../env'
|
|
13
|
+
import { ErrorFocus } from '../../utils/formikErrorFocus'
|
|
13
14
|
|
|
14
15
|
import './style.css'
|
|
15
16
|
|
|
@@ -58,7 +59,6 @@ const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
|
58
59
|
setShowSuccessMessage(true)
|
|
59
60
|
}
|
|
60
61
|
} catch (error) {
|
|
61
|
-
console.log(error)
|
|
62
62
|
} finally {
|
|
63
63
|
setLoading(false)
|
|
64
64
|
}
|
|
@@ -85,6 +85,7 @@ const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
|
85
85
|
onSubmit={handleSubmit}
|
|
86
86
|
>
|
|
87
87
|
<Form>
|
|
88
|
+
<ErrorFocus />
|
|
88
89
|
{showTicketsField && (
|
|
89
90
|
<>
|
|
90
91
|
<div className="field-item">
|
|
@@ -10,6 +10,7 @@ export interface IGroupItem {
|
|
|
10
10
|
required?: boolean;
|
|
11
11
|
component?: ReactNode | JSX.Element | HTMLElement;
|
|
12
12
|
onValidate?: (value: any) => void;
|
|
13
|
+
uniqueId?: string;
|
|
13
14
|
|
|
14
15
|
// aditional props
|
|
15
16
|
[key: string]: any;
|
|
@@ -23,6 +24,7 @@ export interface IFieldData {
|
|
|
23
24
|
groupLabel?: string | JSX.Element | HTMLElement;
|
|
24
25
|
groupLabelClassName?: string;
|
|
25
26
|
id: number;
|
|
27
|
+
uniqueId?: string;
|
|
26
28
|
}
|
|
27
29
|
export interface IBillingInfoData {
|
|
28
30
|
id: number | string;
|
|
@@ -31,4 +33,5 @@ export interface IBillingInfoData {
|
|
|
31
33
|
// optional
|
|
32
34
|
label?: string | JSX.Element;
|
|
33
35
|
labelClassName?: string;
|
|
36
|
+
uniqueId?: string;
|
|
34
37
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { connect, FormikContextType } from 'formik'
|
|
2
|
+
import { Component } from 'react'
|
|
3
|
+
|
|
4
|
+
interface IProps {
|
|
5
|
+
formik: FormikContextType<any>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class ErrorFocusInternal extends Component<IProps> {
|
|
9
|
+
public componentDidUpdate(prevProps: IProps) {
|
|
10
|
+
const { isSubmitting, isValidating, errors } = prevProps.formik
|
|
11
|
+
const keys = Object.keys(errors)
|
|
12
|
+
if (keys.length > 0 && isSubmitting && !isValidating) {
|
|
13
|
+
const selector = `[name="${keys[0]}"]`
|
|
14
|
+
const errorElement = document.querySelector(selector) as HTMLElement
|
|
15
|
+
if (errorElement) {
|
|
16
|
+
errorElement.focus()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public render = () => null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ErrorFocus = connect<{}>(ErrorFocusInternal)
|