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.
Files changed (45) hide show
  1. package/dist/api/index.d.ts +3 -0
  2. package/dist/components/addonsContainer/AddonComponent.d.ts +9 -0
  3. package/dist/components/addonsContainer/adapters/index.d.ts +8 -0
  4. package/dist/components/addonsContainer/index.d.ts +15 -0
  5. package/dist/components/addonsContainer/normalizers/index.d.ts +1 -0
  6. package/dist/components/addonsContainer/utils/index.d.ts +15 -0
  7. package/dist/components/common/SelectField.d.ts +2 -1
  8. package/dist/components/index.d.ts +1 -0
  9. package/dist/components/ticketsContainer/index.d.ts +1 -0
  10. package/dist/index.d.ts +1 -0
  11. package/dist/tf-checkout-react.cjs.development.js +832 -80
  12. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  13. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  14. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  15. package/dist/tf-checkout-react.esm.js +832 -81
  16. package/dist/tf-checkout-react.esm.js.map +1 -1
  17. package/dist/types/add_on.d.ts +7 -0
  18. package/dist/types/checkoutPageConfigs.d.ts +9 -0
  19. package/dist/types/index.d.ts +2 -0
  20. package/dist/types/order-data.d.ts +2 -1
  21. package/dist/utils/createMarkup.d.ts +3 -0
  22. package/dist/utils/index.d.ts +1 -0
  23. package/package.json +1 -1
  24. package/src/api/index.ts +16 -0
  25. package/src/components/addonsContainer/AddonComponent.tsx +49 -0
  26. package/src/components/addonsContainer/adapters/index.tsx +61 -0
  27. package/src/components/addonsContainer/index.tsx +392 -0
  28. package/src/components/addonsContainer/normalizers/index.ts +5 -0
  29. package/src/components/addonsContainer/utils/index.tsx +207 -0
  30. package/src/components/billing-info-container/index.tsx +30 -7
  31. package/src/components/common/SelectField.tsx +18 -8
  32. package/src/components/index.ts +1 -0
  33. package/src/components/orderDetailsContainer/index.tsx +21 -18
  34. package/src/components/paymentContainer/index.tsx +32 -2
  35. package/src/components/ticketsContainer/TicketsSection.tsx +3 -3
  36. package/src/components/ticketsContainer/index.tsx +36 -17
  37. package/src/components/ticketsContainer/utils.ts +3 -3
  38. package/src/index.ts +2 -1
  39. package/src/types/add_on.ts +7 -0
  40. package/src/types/checkoutPageConfigs.ts +9 -0
  41. package/src/types/index.ts +2 -0
  42. package/src/types/order-data.ts +4 -3
  43. package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +7 -6
  44. package/src/utils/createMarkup.ts +1 -0
  45. 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, unknown>,
441
+ values: Record<string, any>,
433
442
  profileData?: any
434
- ): Record<string, unknown> => {
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
- updatedUserData,
519
- 'data.data'
532
+ updatedUserData,
533
+ 'data.data'
520
534
  )
521
535
  const profileDataObj = setLoggedUserData(profileSpecifiedData)
522
536
  window.localStorage.setItem(
523
- 'user_data',
524
- JSON.stringify(profileDataObj)
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 className={element.className}>
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 React from 'react'
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 style={customTheme?.input} htmlFor={field.name} error={!!error && isTouched} shrink={true}>
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
@@ -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
- <div className="personal-link">
238
- <div className="link-item">
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
- <p className="total-referrer">{`So far, you’ve referred ${data.sales_referred} tickets`}</p>
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
- ) : null}
256
- </div>
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 { IOrderData, IPaymentField } from '../../types'
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
- {normalizer(orderData[id], orderData.currency)}
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
- if (result.status === 200) {
278
- const skipBillingPage =
279
- result?.data?.data?.attributes?.skip_billing_page ?? false
280
- const nameIsRequired =
281
- result?.data?.data?.attributes?.names_required ?? false
282
- const ageIsRequired =
283
- result?.data?.data?.attributes?.age_required ?? false
284
- const phoneIsRequired =
285
- result?.data?.data?.attributes?.phone_required ?? false
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
- if (skipBillingPage) {
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 severity="error" onClose={onErrorClose} variant="filled" style={{ width:'350px' }}>
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: number = 10,
3
- minCount: number = 1,
4
- multiplier: number = 1
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'
@@ -0,0 +1,7 @@
1
+ export interface IAddOn {
2
+ id: string | number;
3
+ name: string;
4
+ groupName: string | null;
5
+ price: string;
6
+ quantity: string | number;
7
+ }
@@ -0,0 +1,9 @@
1
+ export interface ICheckoutPageConfigs {
2
+ age_required: boolean;
3
+ event_id: string;
4
+ has_add_on: boolean;
5
+ minimum_age: number | string | null;
6
+ names_required: boolean;
7
+ phone_required: boolean;
8
+ skip_billing_page: boolean;
9
+ }
@@ -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'
@@ -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
- [key: string]: string;
9
+ currency: string;
10
+ add_ons: IAddOn[];
9
11
  }
10
-