tf-checkout-react 1.0.46 → 1.0.50

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.
Files changed (31) hide show
  1. package/dist/api/index.d.ts +1 -0
  2. package/dist/components/billing-info-container/index.d.ts +1 -1
  3. package/dist/components/common/CheckboxField.d.ts +2 -2
  4. package/dist/components/common/CustomField.d.ts +2 -1
  5. package/dist/components/paymentContainer/index.d.ts +1 -1
  6. package/dist/components/stripePayment/index.d.ts +2 -1
  7. package/dist/components/ticketsContainer/index.d.ts +1 -0
  8. package/dist/tf-checkout-react.cjs.development.css +2 -2
  9. package/dist/tf-checkout-react.cjs.development.js +268 -181
  10. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  11. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  12. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  13. package/dist/tf-checkout-react.esm.js +270 -183
  14. package/dist/tf-checkout-react.esm.js.map +1 -1
  15. package/dist/types/billing-info-data.d.ts +2 -2
  16. package/dist/utils/getQueryVariable.d.ts +1 -0
  17. package/package.json +2 -1
  18. package/src/api/index.ts +2 -0
  19. package/src/components/billing-info-container/index.tsx +128 -131
  20. package/src/components/common/CheckboxField.tsx +7 -2
  21. package/src/components/common/CustomField.tsx +17 -7
  22. package/src/components/paymentContainer/index.tsx +31 -3
  23. package/src/components/paymentContainer/style.css +8 -0
  24. package/src/components/stripePayment/index.tsx +48 -19
  25. package/src/components/stripePayment/style.css +6 -0
  26. package/src/components/ticketsContainer/index.tsx +2 -0
  27. package/src/components/waitingList/index.tsx +34 -24
  28. package/src/types/billing-info-data.ts +2 -2
  29. package/src/utils/getQueryVariable.ts +13 -0
  30. package/src/.DS_Store +0 -0
  31. package/src/components/.DS_Store +0 -0
@@ -1,7 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  export interface IGroupItem {
3
3
  name: string;
4
- label: string;
4
+ label: string | JSX.Element;
5
5
  type?: string;
6
6
  className?: string;
7
7
  required?: boolean;
@@ -19,6 +19,6 @@ export interface IFieldData {
19
19
  export interface IBillingInfoData {
20
20
  id: number | string;
21
21
  fields: IFieldData[];
22
- label?: string;
22
+ label?: string | JSX.Element;
23
23
  labelClassName?: string;
24
24
  }
@@ -0,0 +1 @@
1
+ export declare const getQueryVariable: (variable: string) => string | false;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.46",
2
+ "version": "1.0.50",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -78,6 +78,7 @@
78
78
  "jwt-decode": "^3.1.2",
79
79
  "lodash": "^4.17.21",
80
80
  "lodash-es": "^4.17.21",
81
+ "nanoid": "^3.1.30",
81
82
  "react-bootstrap": "^2.0.2",
82
83
  "react-inlinesvg": "^2.3.0"
83
84
  }
package/src/api/index.ts CHANGED
@@ -218,3 +218,5 @@ export const getConfirmationData = (orderHash: string) => publicRequest.get(`/v1
218
218
  export const getStates = (countryId: string) => publicRequest.get(`/countries/${countryId}/states/`)
219
219
 
220
220
  export const addToWaitingList = (id: number, data: any) => publicRequest.post(`/v1/event/${id}/add_to_waiting_list`, data)
221
+
222
+ export const getConditions = (eventId: string) => publicRequest.get(`v1/event/${eventId}/conditions`)
@@ -39,22 +39,11 @@ import { CustomField } from '../common/CustomField'
39
39
  import axios from 'axios'
40
40
  import { CheckboxField } from '../common/CheckboxField'
41
41
  import { CircularProgress } from '@mui/material'
42
+ import { nanoid } from 'nanoid'
43
+ import { getQueryVariable } from '../../utils/getQueryVariable'
42
44
 
43
45
  // const TTFLOGO = require('./logo-ttf.png')
44
46
 
45
- const getQueryVariable = (variable: string) => {
46
- if (typeof window !== 'undefined') {
47
- const query = window.location.search.substring(1)
48
- const vars = query.split('&')
49
- for (let i = 0; i < vars.length; i++) {
50
- const pair = vars[i].split('=')
51
- if (pair[0] === variable) {
52
- return pair[1]
53
- }
54
- }
55
- }
56
- return false
57
- }
58
47
 
59
48
  export interface IBillingInfoPage {
60
49
  data?: IBillingInfoData[];
@@ -62,7 +51,8 @@ export interface IBillingInfoPage {
62
51
  handleSubmit?: (
63
52
  values: FormikValues,
64
53
  formikHelpers: FormikHelpers<FormikValues>,
65
- res?: any
54
+ eventId: any,
55
+ res: any,
66
56
  ) => void;
67
57
 
68
58
  onRegisterSuccess?: (value: {
@@ -175,8 +165,6 @@ const LogicRunner: FC<{
175
165
  return null
176
166
  }
177
167
 
178
- const SectionContainer: FC = ({ children }) => <>{children}</>
179
-
180
168
  export const BillingInfoContainer = ({
181
169
  data = [],
182
170
  ticketHoldersFields = {
@@ -218,7 +206,7 @@ export const BillingInfoContainer = ({
218
206
  const [alreadyHasUser, setAlreadyHasUser] = useState(false)
219
207
  const [userExpired, setUserExpired] = useState(false)
220
208
  const [showModalRegister, setShowModalRegister] = useState(false)
221
- const [ticketsQuantity, setTicketsQuantity] = useState(1)
209
+ const [ticketsQuantity, setTicketsQuantity] = useState<string[]>([])
222
210
  const [userValues, setUserValues] = useState<any>({
223
211
  firstName: '',
224
212
  lastName: '',
@@ -244,7 +232,9 @@ export const BillingInfoContainer = ({
244
232
 
245
233
  const showDOB = getQueryVariable('age_required') === 'true'
246
234
  const showTicketHolderName = getQueryVariable('names_required') === 'true'
235
+ const eventId = getQueryVariable('event_id')
247
236
  const optedInFieldValue: boolean = _get(cartInfoData, 'optedIn', false)
237
+ const hideOptIn: boolean = _get(cartInfoData, 'hide_ttf_opt_in', false)
248
238
 
249
239
  const getQuantity = (cart: any = []) => {
250
240
  let qty: any = 0
@@ -253,6 +243,31 @@ export const BillingInfoContainer = ({
253
243
  })
254
244
  return qty
255
245
  }
246
+
247
+ //just once
248
+ useEffect(() => {
249
+ // fetch countries data
250
+ const fetchCountries = async () => {
251
+ try {
252
+ const res = await getCountries()
253
+ setCustomHeader(res)
254
+ setCountries(
255
+ _map(_get(res, 'data.data'), (item, key) => ({
256
+ label: item,
257
+ value: Number(key),
258
+ }))
259
+ )
260
+ onGetCountriesSuccess(res.data)
261
+ } catch (e) {
262
+ if (axios.isAxiosError(e)) {
263
+ onGetCountriesError(e)
264
+ }
265
+ }
266
+ }
267
+ fetchCountries()
268
+ fetchCart()
269
+ }, [])
270
+
256
271
  // fetch cart data
257
272
  const fetchCart = async () => {
258
273
  try {
@@ -261,7 +276,7 @@ export const BillingInfoContainer = ({
261
276
  const cartInfo = _get(res, 'data.data.attributes')
262
277
  setCartInfo(cartInfo)
263
278
  const { cart = [] } = cartInfo
264
- setTicketsQuantity(getQuantity(cart))
279
+ setTicketsQuantity(new Array(getQuantity(cart)).fill(null).map(() => nanoid()))
265
280
  onGetCartSuccess(res.data)
266
281
  } catch (e) {
267
282
  if (axios.isAxiosError(e)) {
@@ -292,32 +307,8 @@ export const BillingInfoContainer = ({
292
307
  }
293
308
 
294
309
  useEffect(() => {
295
- fetchCart()
296
310
  fetchUserData(access_token)
297
-
298
- // fetch countries data
299
- const fetchCountries = async () => {
300
- try {
301
- const res = await getCountries()
302
- setCustomHeader(res)
303
- setCountries(
304
- _map(_get(res, 'data.data'), (item, key) => ({
305
- label: item,
306
- value: Number(key),
307
- }))
308
- )
309
- onGetCountriesSuccess(res.data)
310
- } catch (e) {
311
- if (axios.isAxiosError(e)) {
312
- onGetCountriesError(e)
313
- }
314
- }
315
- }
316
-
317
- if (!(countries && countries.length)) {
318
- fetchCountries()
319
- }
320
- }, [access_token, countries])
311
+ }, [access_token])
321
312
 
322
313
  return (
323
314
  <>
@@ -350,13 +341,13 @@ export const BillingInfoContainer = ({
350
341
  }
351
342
  }
352
343
  const checkoutBody = createCheckoutDataBody(
353
- ticketsQuantity,
344
+ ticketsQuantity.length,
354
345
  values,
355
346
  { emailLogged, firstNameLogged, lastNameLogged },
356
347
  showDOB
357
348
  )
358
349
  const res = await postOnCheckout(checkoutBody, access_token)
359
- handleSubmit(values, formikHelpers as FormikHelpers<any>, res)
350
+ handleSubmit(values, formikHelpers as FormikHelpers<any>, eventId, res)
360
351
  return
361
352
  }
362
353
 
@@ -416,7 +407,7 @@ export const BillingInfoContainer = ({
416
407
  }
417
408
 
418
409
  const checkoutBody = createCheckoutDataBody(
419
- ticketsQuantity,
410
+ ticketsQuantity.length,
420
411
  values,
421
412
  { emailLogged, firstNameLogged, lastNameLogged },
422
413
  showDOB
@@ -425,7 +416,7 @@ export const BillingInfoContainer = ({
425
416
  checkoutBody,
426
417
  access_token_register
427
418
  )
428
- handleSubmit(values, formikHelpers as FormikHelpers<any>, res)
419
+ handleSubmit(values, formikHelpers as FormikHelpers<any>, eventId, res)
429
420
  } catch (e) {
430
421
  if (axios.isAxiosError(e)) {
431
422
  if (e.response?.data.error === 'invalid_token') {
@@ -477,7 +468,7 @@ export const BillingInfoContainer = ({
477
468
  </div>
478
469
  )}
479
470
  {_map(data, item => {
480
- const { id, label, labelClassName, fields } = item
471
+ const { label, labelClassName, fields } = item
481
472
  if (
482
473
  label === 'Ticket Holders' &&
483
474
  !showTicketHolderName &&
@@ -486,7 +477,7 @@ export const BillingInfoContainer = ({
486
477
  return null
487
478
  }
488
479
  return (
489
- <SectionContainer key={id}>
480
+ <React.Fragment key={nanoid()}>
490
481
  <p className={labelClassName}>{label}</p>
491
482
  {_map(fields, group => {
492
483
  const {
@@ -496,7 +487,7 @@ export const BillingInfoContainer = ({
496
487
  groupLabelClassName,
497
488
  } = group
498
489
  return (
499
- <SectionContainer key={group.id}>
490
+ <React.Fragment key={nanoid()}>
500
491
  {!isLoggedIn && (
501
492
  <div className={groupLabelClassName}>
502
493
  {groupLabel}
@@ -520,103 +511,109 @@ export const BillingInfoContainer = ({
520
511
  if (el.name === 'holderAge' && !showDOB) {
521
512
  return false
522
513
  }
514
+ if (el.name === 'brand_opt_in' && hideOptIn) {
515
+ return false
516
+ }
523
517
  return true
524
518
  }),
525
519
  element =>
526
520
  ['password', 'confirmPassword'].includes(
527
521
  element.name
528
522
  ) && isLoggedIn ? null : (
529
- <div
530
- className={element.className}
531
- key={element.name}
532
- >
533
- {element.component ? (
534
- element.component
535
- ) : (
536
- <Field
537
- name={element.name}
538
- label={element.label}
539
- type={element.type}
540
- validate={combineValidators(
541
- element.required
542
- ? requiredValidator
543
- : () => {},
544
- element.onValidate
545
- ? element.onValidate
546
- : () => {}
547
- )}
548
- component={
549
- element.type === 'checkbox'
550
- ? CheckboxField
551
- : CustomField
552
- }
553
- selectOptions={
554
- element.name === 'country'
555
- ? countries
556
- : element.name === 'state'
557
- ? states
558
- : []
559
- }
560
- />
561
- )}
562
- </div>
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}
527
+ <div className={element.className}>
528
+ {element.component ? (
529
+ element.component
530
+ ) : (
531
+ <Field
532
+ name={element.name}
533
+ label={element.label}
534
+ type={element.type}
535
+ validate={combineValidators(
536
+ element.required
537
+ ? requiredValidator
538
+ : () => {},
539
+ element.onValidate
540
+ ? element.onValidate
541
+ : () => {}
542
+ )}
543
+ component={
544
+ element.type === 'checkbox'
545
+ ? CheckboxField
546
+ : CustomField
547
+ }
548
+ selectOptions={
549
+ element.name === 'country'
550
+ ? countries
551
+ : element.name === 'state'
552
+ ? states
553
+ : []
554
+ }
555
+ theme={theme}
556
+ />
557
+ )}
558
+ </div>
559
+ </React.Fragment>
563
560
  )
564
561
  )}
565
562
  </div>
566
- </SectionContainer>
563
+ </React.Fragment>
567
564
  )
568
565
  })}
569
- </SectionContainer>
566
+ </React.Fragment>
570
567
  )
571
568
  })}
572
-
573
569
  {showTicketHolderName && (
574
- <div>
575
- <p>{ticketHoldersFields.label}</p>
576
- {_map(new Array(ticketsQuantity), (_item, index) => (
577
- <div key={index}>
578
- <h5>Ticket {index + 1}</h5>
579
- {_map(ticketHoldersFields.fields, group => {
580
- const { groupClassname, groupItems } = group
581
- return (
582
- <div key={group.id}>
583
- <div className={groupClassname}>
584
- {_map(groupItems, element => (
585
- <div
586
- className={element.className}
587
- key={`${element.name}-${index}`}
588
- >
589
- <Field
590
- name={`${element.name}-${index}`}
591
- key={`${element.name}-${index}`}
592
- label={element.label}
593
- type={element.type}
594
- required={true}
595
- component={
596
- element.type === 'checkbox'
597
- ? CheckboxField
598
- : CustomField
599
- }
600
- validate={combineValidators(
601
- element.required
602
- ? requiredValidator
603
- : () => {},
604
- element.onValidate
605
- ? element.onValidate
606
- : () => {}
607
- )}
608
- />
609
- </div>
610
- ))}
570
+ <React.Fragment key={nanoid()}>
571
+ <div className="ticket-holders-fields">
572
+ <p>{ticketHoldersFields.label}</p>
573
+ {_map(ticketsQuantity, (_item, index) => (
574
+ <div key={nanoid()}>
575
+ <h5>Ticket {index + 1}</h5>
576
+ {_map(ticketHoldersFields.fields, group => {
577
+ const { groupClassname, groupItems } = group
578
+ return (
579
+ <div key={nanoid()}>
580
+ <div className={groupClassname}>
581
+ {_map(groupItems, element => (
582
+ <div
583
+ className={element.className}
584
+ key={nanoid()}
585
+ >
586
+ <Field
587
+ name={`${element.name}-${index}`}
588
+ label={element.label}
589
+ type={element.type}
590
+ required={true}
591
+ component={
592
+ element.type === 'checkbox'
593
+ ? CheckboxField
594
+ : CustomField
595
+ }
596
+ validate={combineValidators(
597
+ element.required
598
+ ? requiredValidator
599
+ : () => {},
600
+ element.onValidate
601
+ ? element.onValidate
602
+ : () => {}
603
+ )}
604
+ />
605
+ </div>
606
+ ))}
607
+ </div>
611
608
  </div>
612
- </div>
613
- )
614
- })}
615
- </div>
616
- ))}
617
- </div>
609
+ )
610
+ })}
611
+ </div>
612
+ ))}
613
+ </div>
614
+ </React.Fragment>
618
615
  )}
619
- <div className="button-container">
616
+ <div className="button-container" key={nanoid()}>
620
617
  <LoadingButton
621
618
  type="submit"
622
619
  variant="contained"
@@ -6,7 +6,7 @@ import { FieldInputProps } from 'formik'
6
6
 
7
7
  export interface ICheckboxField {
8
8
  label: string | number | JSX.Element;
9
- field: FieldInputProps<any>;
9
+ field?: FieldInputProps<any>;
10
10
  }
11
11
 
12
12
  interface IOtherProps {
@@ -16,9 +16,14 @@ interface IOtherProps {
16
16
  export const CheckboxField = ({
17
17
  label,
18
18
  field,
19
+ selectOptions,
20
+ theme,
19
21
  ...rest
20
22
  }: ICheckboxField & IOtherProps) => (
21
23
  <FormGroup>
22
- <FormControlLabel control={<Checkbox {...field} {...rest} />} label={label} />
24
+ <FormControlLabel
25
+ control={<Checkbox {...field} {...rest} />}
26
+ label={label}
27
+ />
23
28
  </FormGroup>
24
29
  )
@@ -17,6 +17,7 @@ export interface ICustomField {
17
17
 
18
18
  field: FieldInputProps<any>;
19
19
  form: FormikProps<any>;
20
+ theme: 'dark' | 'light';
20
21
 
21
22
  // optional
22
23
  type?: string;
@@ -29,9 +30,9 @@ interface IOtherProps {
29
30
 
30
31
  const useStyles = makeStyles({
31
32
  input: {
32
- "&::placeholder": {
33
- color: "gray",
34
- }
33
+ '&::placeholder': {
34
+ color: 'gray',
35
+ },
35
36
  },
36
37
  })
37
38
 
@@ -41,6 +42,7 @@ export const CustomField = ({
41
42
  field,
42
43
  selectOptions = [] as ISelectOption[],
43
44
  form: { touched, errors },
45
+ theme,
44
46
  }: ICustomField & IOtherProps) => {
45
47
  const isSelectField = type === 'select'
46
48
  const isShrink = Boolean(field.value) || type === 'date' || type === 'select'
@@ -60,15 +62,23 @@ export const CustomField = ({
60
62
  InputLabelProps={{ shrink: isShrink }}
61
63
  InputProps={{
62
64
  classes: {
63
- input: classes.input
64
- }
65
+ input: classes.input,
66
+ },
67
+ }}
68
+ SelectProps={{
69
+ native: true,
70
+ className: theme,
71
+ MenuProps: { className: theme },
65
72
  }}
66
- SelectProps={{ native: true }}
67
73
  {...field}
68
74
  >
69
75
  {isSelectField
70
76
  ? _map(selectOptions, option => (
71
- <option key={option.value} value={option.value} disabled={option.disabled}>
77
+ <option
78
+ key={option.value}
79
+ value={option.value}
80
+ disabled={option.disabled}
81
+ >
72
82
  {option.label}
73
83
  </option>
74
84
  ))
@@ -8,12 +8,14 @@ import _map from 'lodash/map'
8
8
  import _get from 'lodash/get'
9
9
  import _identity from 'lodash/identity'
10
10
  import { ENV } from '../../env'
11
+ import { nanoid } from 'nanoid'
12
+ import { getQueryVariable } from '../../utils/getQueryVariable'
11
13
 
12
14
  import './style.css'
13
15
  import StripePayment from '../stripePayment'
14
16
  import { IOrderData, IPaymentField } from '../../types'
15
17
 
16
- import { getPaymentData, handlePaymentSuccess } from '../../api'
18
+ import { getPaymentData, handlePaymentSuccess, getConditions } from '../../api'
17
19
 
18
20
  const testId = ENV.STRIPE_PUBLISHABLE_KEY || 'pk_test_3Ov1P1oP33A1cxaSjxWE0VjT'
19
21
  const stripePromise = loadStripe(
@@ -47,7 +49,7 @@ const initialReviewValues = {
47
49
  payment_method: {
48
50
  stripe_client_secret: '',
49
51
  },
50
- billing_info: {}
52
+ billing_info: {},
51
53
  }
52
54
 
53
55
  export const PaymentContainer = ({
@@ -59,16 +61,21 @@ export const PaymentContainer = ({
59
61
  onErrorClose = _identity,
60
62
  onGetPaymentDataSuccess = () => {},
61
63
  onGetPaymentDataError = () => {},
62
- onPaymentError = () => {}
64
+ onPaymentError = () => {},
63
65
  }: IPaymentPage) => {
64
66
  const [reviewData, setReviewData] = useState(initialReviewValues)
65
67
  const [orderData, setOrderData] = useState(initialOrderValues)
66
68
  const [error, setError] = useState(null)
67
69
  const [paymentIsLoading, setPaymentIsLoading] = useState(false)
70
+ const [conditions, setConditions] = useState<{ id: string; text: string }[]>(
71
+ []
72
+ )
68
73
 
69
74
  const showFormTitle: boolean = Boolean(formTitle)
70
75
  const showErrorText: boolean = Boolean(errorText)
71
76
 
77
+ const eventId = getQueryVariable('event_id')
78
+
72
79
  useEffect(() => {
73
80
  const { hash } = checkoutData;
74
81
  (async () => {
@@ -99,6 +106,26 @@ export const PaymentContainer = ({
99
106
  })()
100
107
  }, [checkoutData])
101
108
 
109
+ //just once
110
+ useEffect(() => {
111
+ // fetch conditions data
112
+ const fetchConditions = async () => {
113
+ if (eventId) {
114
+ const res = await getConditions(eventId)
115
+ const conditionsInfo = _get(res, 'data.data.attributes')
116
+ setConditions(
117
+ conditionsInfo
118
+ ? conditionsInfo.map((item: string) => ({
119
+ id: nanoid(),
120
+ text: item,
121
+ }))
122
+ : []
123
+ )
124
+ }
125
+ }
126
+ fetchConditions()
127
+ }, [])
128
+
102
129
  // 1. get payment data ---> v1/order/${hash}/review/ done
103
130
  // 2. handle payment ---> v1/order/${orderHash}/pay/
104
131
  // 3. redirect to confirmation page
@@ -163,6 +190,7 @@ export const PaymentContainer = ({
163
190
  billing_info={reviewData.billing_info}
164
191
  isLoading={paymentIsLoading}
165
192
  handleSetLoading={value => setPaymentIsLoading(value)}
193
+ conditions={conditions}
166
194
  />
167
195
  </Elements>
168
196
  </div>
@@ -45,3 +45,11 @@
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
+ }