tf-checkout-react 1.0.50 → 1.0.51

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.
@@ -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
  }
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare const ErrorFocus: import("react").ComponentType<{}>;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.50",
2
+ "version": "1.0.51",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
package/src/.DS_Store ADDED
Binary file
Binary file
@@ -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 hideOptIn: boolean = _get(cartInfoData, 'hide_ttf_opt_in', false)
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(new Array(getQuantity(cart)).fill(null).map(() => nanoid()))
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
- data,
330
+ dataWithUniqueIds,
318
331
  {
319
332
  ...initialValues,
320
333
  email: emailLogged,
@@ -347,7 +360,12 @@ export const BillingInfoContainer = ({
347
360
  showDOB
348
361
  )
349
362
  const res = await postOnCheckout(checkoutBody, access_token)
350
- handleSubmit(values, formikHelpers as FormikHelpers<any>, eventId, res)
363
+ handleSubmit(
364
+ values,
365
+ formikHelpers as FormikHelpers<any>,
366
+ eventId,
367
+ res
368
+ )
351
369
  return
352
370
  }
353
371
 
@@ -416,7 +434,12 @@ export const BillingInfoContainer = ({
416
434
  checkoutBody,
417
435
  access_token_register
418
436
  )
419
- handleSubmit(values, formikHelpers as FormikHelpers<any>, eventId, res)
437
+ handleSubmit(
438
+ values,
439
+ formikHelpers as FormikHelpers<any>,
440
+ eventId,
441
+ res
442
+ )
420
443
  } catch (e) {
421
444
  if (axios.isAxiosError(e)) {
422
445
  if (e.response?.data.error === 'invalid_token') {
@@ -434,6 +457,7 @@ export const BillingInfoContainer = ({
434
457
  >
435
458
  {(props: FormikProps<any>) => (
436
459
  <Form onSubmit={props.handleSubmit}>
460
+ <ErrorFocus />
437
461
  <LogicRunner
438
462
  values={props.values}
439
463
  setStates={setStates}
@@ -467,7 +491,7 @@ export const BillingInfoContainer = ({
467
491
  </div>
468
492
  </div>
469
493
  )}
470
- {_map(data, item => {
494
+ {_map(dataWithUniqueIds, item => {
471
495
  const { label, labelClassName, fields } = item
472
496
  if (
473
497
  label === 'Ticket Holders' &&
@@ -477,7 +501,7 @@ export const BillingInfoContainer = ({
477
501
  return null
478
502
  }
479
503
  return (
480
- <React.Fragment key={nanoid()}>
504
+ <React.Fragment key={item.uniqueId}>
481
505
  <p className={labelClassName}>{label}</p>
482
506
  {_map(fields, group => {
483
507
  const {
@@ -487,7 +511,7 @@ export const BillingInfoContainer = ({
487
511
  groupLabelClassName,
488
512
  } = group
489
513
  return (
490
- <React.Fragment key={nanoid()}>
514
+ <React.Fragment key={group.uniqueId}>
491
515
  {!isLoggedIn && (
492
516
  <div className={groupLabelClassName}>
493
517
  {groupLabel}
@@ -511,7 +535,7 @@ export const BillingInfoContainer = ({
511
535
  if (el.name === 'holderAge' && !showDOB) {
512
536
  return false
513
537
  }
514
- if (el.name === 'brand_opt_in' && hideOptIn) {
538
+ if (el.name === 'ttf_opt_in' && hideTtfOptIn) {
515
539
  return false
516
540
  }
517
541
  return true
@@ -520,10 +544,14 @@ export const BillingInfoContainer = ({
520
544
  ['password', 'confirmPassword'].includes(
521
545
  element.name
522
546
  ) && isLoggedIn ? null : (
523
- <React.Fragment key={nanoid()}>
524
- {element.name === 'email'
525
- ? <div className="email-checking">IMPORTANT: Please double check that your email address is correct. It's where we send your confirmation and e-tickets to!</div>
526
- : null}
547
+ <React.Fragment key={element.uniqueId}>
548
+ {element.name === 'email' ? (
549
+ <div className="email-checking">
550
+ {`IMPORTANT: Please double check that your
551
+ email address is correct. It's where we
552
+ send your confirmation and e-tickets to!`}
553
+ </div>
554
+ ) : null}
527
555
  <div className={element.className}>
528
556
  {element.component ? (
529
557
  element.component
@@ -532,17 +560,15 @@ export const BillingInfoContainer = ({
532
560
  name={element.name}
533
561
  label={element.label}
534
562
  type={element.type}
535
- validate={combineValidators(
536
- element.required
537
- ? requiredValidator
538
- : () => {},
539
- element.onValidate
540
- ? element.onValidate
541
- : () => {}
563
+ validate={getValidateFunctions(
564
+ element,
565
+ states
542
566
  )}
543
567
  component={
544
568
  element.type === 'checkbox'
545
569
  ? CheckboxField
570
+ : element.type === 'select'
571
+ ? SelectField
546
572
  : CustomField
547
573
  }
548
574
  selectOptions={
@@ -567,21 +593,21 @@ export const BillingInfoContainer = ({
567
593
  )
568
594
  })}
569
595
  {showTicketHolderName && (
570
- <React.Fragment key={nanoid()}>
596
+ <React.Fragment>
571
597
  <div className="ticket-holders-fields">
572
598
  <p>{ticketHoldersFields.label}</p>
573
599
  {_map(ticketsQuantity, (_item, index) => (
574
- <div key={nanoid()}>
600
+ <div key={_item}>
575
601
  <h5>Ticket {index + 1}</h5>
576
602
  {_map(ticketHoldersFields.fields, group => {
577
603
  const { groupClassname, groupItems } = group
578
604
  return (
579
- <div key={nanoid()}>
605
+ <div key={group.uniqueId}>
580
606
  <div className={groupClassname}>
581
607
  {_map(groupItems, element => (
582
608
  <div
583
609
  className={element.className}
584
- key={nanoid()}
610
+ key={element.uniqueId}
585
611
  >
586
612
  <Field
587
613
  name={`${element.name}-${index}`}
@@ -613,7 +639,7 @@ export const BillingInfoContainer = ({
613
639
  </div>
614
640
  </React.Fragment>
615
641
  )}
616
- <div className="button-container" key={nanoid()}>
642
+ <div className="button-container">
617
643
  <LoadingButton
618
644
  type="submit"
619
645
  variant="contained"
@@ -99,4 +99,8 @@ button {
99
99
  .billing-info-container .main-header {
100
100
  font-size: 1.5rem;
101
101
  }
102
+ }
103
+
104
+ .email-checking {
105
+ margin-bottom: 15px;
102
106
  }
@@ -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;
@@ -81,6 +86,7 @@ export const setLoggedUserData = (data: IUserData) => ({
81
86
  first_name: data.firstName,
82
87
  last_name: data.lastName,
83
88
  email: data.email,
89
+ confirmEmail: data.email,
84
90
  city: data?.city || '',
85
91
  country: data?.country || '',
86
92
  phone: data?.phone || '',
@@ -150,3 +156,41 @@ export const createCheckoutDataBody = (
150
156
  }
151
157
  return body
152
158
  }
159
+
160
+ export const getValidateFunctions = (
161
+ element: IGroupItem,
162
+ states: Array<{ [key: string]: any }>
163
+ ) => {
164
+ const validationFunctions: any[] = []
165
+
166
+ if (element.required) {
167
+ if (
168
+ element.name !== 'state' ||
169
+ (element.name === 'state' && states.length)
170
+ ) {
171
+ validationFunctions.push(requiredValidator)
172
+ }
173
+ }
174
+
175
+ if (element.onValidate) {
176
+ validationFunctions.push(element.onValidate)
177
+ }
178
+
179
+ return combineValidators(...validationFunctions)
180
+ }
181
+
182
+ export const assingUniqueIds = (data: any): any => {
183
+ if (_get(data[0], 'uniqueId')) {
184
+ return data
185
+ }
186
+
187
+ return _map(data, (item: any) => {
188
+ _forEach(item, (itemValue: string, key) => {
189
+ if (_isArray(itemValue)) {
190
+ item[key] = assingUniqueIds(itemValue)
191
+ }
192
+ })
193
+
194
+ return { ...item, uniqueId: nanoid() }
195
+ })
196
+ }
@@ -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'
@@ -46,8 +47,10 @@ export const CustomField = ({
46
47
  }: ICustomField & IOtherProps) => {
47
48
  const isSelectField = type === 'select'
48
49
  const isShrink = Boolean(field.value) || type === 'date' || type === 'select'
49
- const isTouched = Boolean(_get(touched, field.name))
50
50
  const error = _get(errors, field.name)
51
+ const isTouched =
52
+ Boolean(_get(touched, field.name)) ||
53
+ (_includes(field.name, 'holder') && !!error)
51
54
  const classes = useStyles()
52
55
 
53
56
  return (
@@ -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}>
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
+ }
@@ -45,11 +45,3 @@
45
45
  padding-top: 20px;
46
46
  text-align: center;
47
47
  }
48
-
49
- .conditions-block {
50
- border: 1px solid #e3e3e3;
51
- border-radius: 4px;
52
- background-color: #f5f5f5;
53
- padding: 19px;
54
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
55
- }
@@ -2,6 +2,10 @@
2
2
  background: #232323;
3
3
  border-radius: 8px;
4
4
  padding: 15px;
5
+ width: 50%;
6
+ margin: 0 auto;
7
+ min-width: 325px;
8
+ margin-bottom: 20px;
5
9
  }
6
10
  .card_form_inner .card_label_text {
7
11
  color: #fff;
@@ -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)