tf-checkout-react 1.6.6 → 1.7.1

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 (131) hide show
  1. package/README.md +401 -59
  2. package/dist/adapters/customFields.d.ts +1 -0
  3. package/dist/api/checkout.d.ts +2 -0
  4. package/dist/api/common.d.ts +1 -0
  5. package/dist/api/index.d.ts +2 -0
  6. package/dist/api/preRegistrationComplete.d.ts +1 -1
  7. package/dist/components/addonsContainer/AddonComponent.d.ts +6 -1
  8. package/dist/components/addonsContainer/SimpleAddonsContainer.d.ts +17 -0
  9. package/dist/components/addonsContainer/index.d.ts +6 -1
  10. package/dist/components/billing-info-container/hooks/index.d.ts +3 -0
  11. package/dist/components/billing-info-container/hooks/usePaymentContext.d.ts +5 -0
  12. package/dist/components/billing-info-container/hooks/usePaymentRedirect.d.ts +14 -0
  13. package/dist/components/billing-info-container/hooks/useStripePayment.d.ts +18 -0
  14. package/dist/components/billing-info-container/index.d.ts +13 -2
  15. package/dist/components/billing-info-container/utils.d.ts +25 -1
  16. package/dist/components/common/DatePickerField.d.ts +7 -1
  17. package/dist/components/confirmationContainer/index.d.ts +4 -1
  18. package/dist/components/countdown/index.d.ts +1 -1
  19. package/dist/components/forgotPasswordModal/index.d.ts +2 -1
  20. package/dist/components/myTicketsContainer/index.d.ts +3 -2
  21. package/dist/components/orderDetailsContainer/index.d.ts +8 -1
  22. package/dist/components/paymentContainer/OrderDetails.d.ts +9 -0
  23. package/dist/components/paymentContainer/handlePayment.d.ts +15 -0
  24. package/dist/components/paymentContainer/index.d.ts +12 -6
  25. package/dist/components/preRegistration/FieldsSection.d.ts +7 -1
  26. package/dist/components/preRegistration/PreRegistrationComplete.d.ts +6 -0
  27. package/dist/components/preRegistration/constants.d.ts +2 -2
  28. package/dist/components/preRegistration/index.d.ts +6 -0
  29. package/dist/components/resetPasswordContainer/index.d.ts +2 -2
  30. package/dist/components/ticketsContainer/InfoIcon.d.ts +5 -0
  31. package/dist/components/ticketsContainer/TicketsSection.d.ts +3 -2
  32. package/dist/components/ticketsContainer/TimeSlotsSection.d.ts +25 -0
  33. package/dist/components/ticketsContainer/index.d.ts +29 -5
  34. package/dist/components/timerWidget/index.d.ts +2 -1
  35. package/dist/constants/index.d.ts +5 -0
  36. package/dist/index.d.ts +4 -1
  37. package/dist/tf-checkout-react.cjs.development.js +11231 -9563
  38. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  39. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  40. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  41. package/dist/tf-checkout-react.esm.js +11194 -9529
  42. package/dist/tf-checkout-react.esm.js.map +1 -1
  43. package/dist/tf-checkout-styles.css +1 -1
  44. package/dist/types/add_on.d.ts +1 -0
  45. package/dist/types/checkoutPageConfigs.d.ts +1 -1
  46. package/dist/types/order-data.d.ts +3 -1
  47. package/dist/utils/auth.d.ts +8 -0
  48. package/dist/utils/customFields.d.ts +11 -0
  49. package/dist/utils/getDomain.d.ts +1 -1
  50. package/dist/utils/index.d.ts +1 -1
  51. package/dist/utils/setConfigs.d.ts +1 -0
  52. package/package.json +14 -8
  53. package/src/adapters/customFields.ts +7 -1
  54. package/src/api/auth.ts +2 -1
  55. package/src/api/checkout.ts +9 -4
  56. package/src/api/common.ts +49 -2
  57. package/src/api/index.ts +1 -0
  58. package/src/api/interceptors.ts +7 -23
  59. package/src/api/preRegistrationComplete.ts +1 -1
  60. package/src/api/publicRequest.ts +10 -0
  61. package/src/components/addonsContainer/AddonComponent.tsx +96 -11
  62. package/src/components/addonsContainer/SimpleAddonsContainer.tsx +420 -0
  63. package/src/components/addonsContainer/index.tsx +198 -47
  64. package/src/components/billing-info-container/hooks/index.ts +3 -0
  65. package/src/components/billing-info-container/hooks/usePaymentContext.ts +22 -0
  66. package/src/components/billing-info-container/hooks/usePaymentRedirect.ts +147 -0
  67. package/src/components/billing-info-container/hooks/useStripePayment.ts +121 -0
  68. package/src/components/billing-info-container/index.tsx +827 -406
  69. package/src/components/billing-info-container/{utils.ts → utils.tsx} +119 -0
  70. package/src/components/common/CheckboxField/index.tsx +1 -1
  71. package/src/components/common/CustomField.tsx +38 -2
  72. package/src/components/common/DatePickerField.tsx +25 -10
  73. package/src/components/common/SnackbarAlert.tsx +32 -34
  74. package/src/components/confirmationContainer/config.ts +3 -3
  75. package/src/components/confirmationContainer/index.tsx +20 -1
  76. package/src/components/confirmationContainer/social-buttons.tsx +5 -3
  77. package/src/components/confirmationContainer/style.css +9 -5
  78. package/src/components/countdown/index.tsx +22 -22
  79. package/src/components/delegationsContainer/IssueComponent.tsx +2 -1
  80. package/src/components/forgotPasswordModal/index.tsx +44 -13
  81. package/src/components/loginForm/index.tsx +1 -1
  82. package/src/components/loginModal/index.tsx +19 -27
  83. package/src/components/loginModal/style.css +3 -1
  84. package/src/components/myTicketsContainer/index.tsx +13 -9
  85. package/src/components/orderDetailsContainer/index.tsx +206 -174
  86. package/src/components/paymentContainer/OrderDetails.tsx +257 -0
  87. package/src/components/paymentContainer/handlePayment.ts +86 -0
  88. package/src/components/paymentContainer/index.tsx +299 -259
  89. package/src/components/paymentContainer/style.css +141 -0
  90. package/src/components/preRegistration/FieldsSection.tsx +8 -0
  91. package/src/components/preRegistration/PreRegistrationComplete.tsx +128 -118
  92. package/src/components/preRegistration/PreRegistrationInformations.tsx +21 -15
  93. package/src/components/preRegistration/constants.tsx +10 -4
  94. package/src/components/preRegistration/index.tsx +194 -174
  95. package/src/components/registerForm/constants.tsx +3 -1
  96. package/src/components/registerForm/index.tsx +3 -3
  97. package/src/components/registerModal/index.tsx +47 -72
  98. package/src/components/resetPasswordContainer/index.tsx +20 -14
  99. package/src/components/seatMapContainer/TicketsSection.tsx +2 -2
  100. package/src/components/signupModal/index.tsx +13 -6
  101. package/src/components/ticketResale/index.tsx +7 -0
  102. package/src/components/ticketsContainer/InfoIcon.tsx +35 -0
  103. package/src/components/ticketsContainer/PromoCodeSection.tsx +34 -28
  104. package/src/components/ticketsContainer/TicketRow.tsx +1 -1
  105. package/src/components/ticketsContainer/TicketsSection.tsx +189 -57
  106. package/src/components/ticketsContainer/TimeSlotsSection.tsx +120 -0
  107. package/src/components/ticketsContainer/index.tsx +268 -106
  108. package/src/components/timerWidget/index.tsx +15 -3
  109. package/src/constants/index.ts +2 -0
  110. package/src/env.ts +14 -6
  111. package/src/hoc/CustomFields/index.tsx +9 -1
  112. package/src/index.ts +7 -2
  113. package/src/types/add_on.ts +1 -0
  114. package/src/types/api/cart.d.ts +8 -0
  115. package/src/types/api/checkout.d.ts +58 -7
  116. package/src/types/api/common.d.ts +30 -0
  117. package/src/types/api/orders.d.ts +19 -3
  118. package/src/types/api/payment.d.ts +6 -2
  119. package/src/types/api/preRegistrationComplete.d.ts +2 -2
  120. package/src/types/checkoutPageConfigs.ts +1 -1
  121. package/src/types/order-data.ts +3 -1
  122. package/src/types/pre-registration-complete.d.ts +6 -1
  123. package/src/utils/auth.ts +32 -0
  124. package/src/utils/cookies.ts +42 -11
  125. package/src/utils/customFields.ts +22 -0
  126. package/src/utils/getDomain.ts +10 -4
  127. package/src/utils/index.ts +1 -1
  128. package/src/utils/setConfigs.ts +3 -1
  129. package/dist/components/stripePayment/index.d.ts +0 -24
  130. package/src/components/stripePayment/index.tsx +0 -281
  131. package/src/components/stripePayment/style.css +0 -60
@@ -1,31 +1,38 @@
1
- import React, { FC, useState } from 'react'
2
- import { Field, Form, Formik } from 'formik'
1
+ import './style.css'
2
+
3
3
  import { CircularProgress } from '@mui/material'
4
- import { CustomField } from '../common/CustomField'
5
4
  import axios, { AxiosError } from 'axios'
5
+ import { Field, Form, Formik } from 'formik'
6
+ import _identity from 'lodash/identity'
7
+ import React, { FC, useState } from 'react'
6
8
  import * as Yup from 'yup'
9
+
7
10
  import { resetPassword } from '../../api'
8
- import './style.css'
11
+ import { CustomField } from '../common/CustomField'
9
12
 
10
13
  interface IResetPasswordProps {
11
- token?: string
12
- onResetPasswordSuccess?: (res: any) => void
13
- onResetPasswordError?: (e: AxiosError) => void
14
+ token?: string;
15
+ onResetPasswordSuccess?: (res: any) => void;
16
+ onResetPasswordError?: (e: AxiosError) => void;
14
17
  }
15
18
 
16
19
  const Schema = Yup.object().shape({
17
20
  password: Yup.string()
18
- .min(6, 'Password must have 5+ characters')
19
- .required('Required'),
20
- password_confirmation: Yup.string()
21
+ .min(8, 'Password must have 8+ characters')
22
+ .required('Required')
23
+ .matches(
24
+ new RegExp('^(?=.*[@$!%*#?&])'),
25
+ 'Password must contain at least one special character'
26
+ ),
27
+ password_confirmation: Yup.string()
21
28
  .required('Required')
22
29
  .oneOf([Yup.ref('password'), null], 'Passwords must match'),
23
30
  })
24
31
 
25
32
  export const ResetPasswordContainer: FC<IResetPasswordProps> = ({
26
33
  token: tokenProps = '',
27
- onResetPasswordSuccess = () => {},
28
- onResetPasswordError = () => {},
34
+ onResetPasswordSuccess = _identity,
35
+ onResetPasswordError = _identity,
29
36
  }) => {
30
37
  const [loading, setLoading] = useState(false)
31
38
  return (
@@ -42,8 +49,7 @@ export const ResetPasswordContainer: FC<IResetPasswordProps> = ({
42
49
  token = tokenProps
43
50
  } else {
44
51
  if (typeof window !== 'undefined') {
45
- const params: URLSearchParams = new URL(`${window.location}`)
46
- .searchParams
52
+ const params: URLSearchParams = new URL(`${window.location}`).searchParams
47
53
  token = params.get('token')
48
54
  }
49
55
  }
@@ -1,11 +1,11 @@
1
1
  import { CSSProperties } from '@emotion/serialize'
2
- import { createTheme, Select, SelectChangeEvent, ThemeOptions } from '@mui/material'
2
+ import { Select, SelectChangeEvent, ThemeOptions } from '@mui/material'
3
3
  import CircularProgress from '@mui/material/CircularProgress'
4
4
  import FormControl from '@mui/material/FormControl'
5
5
  import InputLabel from '@mui/material/InputLabel'
6
6
  import MenuItem from '@mui/material/MenuItem'
7
+ import { createTheme, ThemeProvider } from '@mui/material/styles'
7
8
  import Tooltip from '@mui/material/Tooltip'
8
- import { ThemeProvider } from '@mui/private-theming'
9
9
  import _find from 'lodash/find'
10
10
  import _identity from 'lodash/identity'
11
11
  import _isEmpty from 'lodash/isEmpty'
@@ -3,6 +3,7 @@ import './style.css'
3
3
  import { Box, CircularProgress, Modal } from '@mui/material'
4
4
  import axios, { AxiosError } from 'axios'
5
5
  import { Field, Form, Formik } from 'formik'
6
+ import _identity from 'lodash/identity'
6
7
  import React, { FC, useState } from 'react'
7
8
  import * as Yup from 'yup'
8
9
 
@@ -43,17 +44,23 @@ const SignupSchema = Yup.object().shape({
43
44
  firstName: Yup.string().required('Required'),
44
45
  lastName: Yup.string().required('Required'),
45
46
  email: Yup.string().email('Invalid email').required('Required'),
46
- password: Yup.string().min(6, 'Password must have 5+ characters').required('Required'),
47
+ password: Yup.string()
48
+ .min(8, 'Password must have 8+ characters')
49
+ .required('Required')
50
+ .matches(
51
+ new RegExp('^(?=.*[@$!%*#?&])'),
52
+ 'Password must contain at least one special character'
53
+ ),
47
54
  confirmPassword: Yup.string()
48
55
  .required('Required')
49
56
  .oneOf([Yup.ref('password'), null], 'Passwords must match'),
50
57
  })
51
58
 
52
59
  export const SignupModal: FC<ISignupProps> = ({
53
- onClose = () => {},
54
- onLogin = () => {},
55
- onRegisterSuccess = () => {},
56
- onRegisterError = () => {},
60
+ onClose = _identity,
61
+ onLogin = _identity,
62
+ onRegisterSuccess = _identity,
63
+ onRegisterError = _identity,
57
64
  showPoweredByImage = false,
58
65
  }) => {
59
66
  const [loading, setLoading] = useState(false)
@@ -89,7 +96,7 @@ export const SignupModal: FC<ISignupProps> = ({
89
96
  }
90
97
  }
91
98
 
92
- const _onClose = loading ? () => {} : onClose
99
+ const _onClose = loading ? _identity : onClose
93
100
 
94
101
  return (
95
102
  <Modal
@@ -33,6 +33,7 @@ export const TicketResaleContainer = ({
33
33
  const params: URLSearchParams = new URL(`${window.location}`).searchParams
34
34
  const hash = params.get('invitation_hash') || orderHash || null
35
35
  const isDeclined = params.get('decline') || false
36
+ window.localStorage.removeItem('checkoutAdditionalConfigs')
36
37
 
37
38
  if (hash) {
38
39
  // Process of declining ticket purchase invitation
@@ -53,6 +54,12 @@ export const TicketResaleContainer = ({
53
54
  try {
54
55
  const response = await processTicket(hash)
55
56
  const event_id = _get(response.data.data.attributes, 'event_id')
57
+ const eventHasAddons = _get(response.data.data.attributes, 'has_add_on')
58
+ const invitationIncludesAddons = _get(response.data.data.attributes, 'include_add_on')
59
+ window.localStorage.setItem('checkoutAdditionalConfigs', JSON.stringify({
60
+ resale: true,
61
+ resaleWithAddons: eventHasAddons && invitationIncludesAddons,
62
+ }))
56
63
 
57
64
  onProcessTicketSuccess(response.data)
58
65
  if (defaultRedirection) {
@@ -0,0 +1,35 @@
1
+ import React from "react"
2
+
3
+ const InfoIcon = ({ size = 17 }) => (
4
+ <svg
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ width={size}
7
+ height={size}
8
+ viewBox="0 0 17 17"
9
+ fill="none"
10
+ >
11
+ <circle
12
+ cx="8.5"
13
+ cy="8.5"
14
+ r="8"
15
+ stroke="black"
16
+ strokeWidth="1"
17
+ />
18
+ <rect
19
+ x="7.5"
20
+ y="4"
21
+ width="2"
22
+ height="2"
23
+ fill="black"
24
+ />
25
+ <rect
26
+ x="7.5"
27
+ y="7"
28
+ width="2"
29
+ height="5"
30
+ fill="black"
31
+ />
32
+ </svg>
33
+ )
34
+
35
+ export default InfoIcon
@@ -33,38 +33,39 @@ export const PromoCodeSection = ({
33
33
  showAlertIcons
34
34
  }: IPromoCodeSectionProps) => {
35
35
  const isPromoCodeHasValue = !!code.trim()
36
+ const appliedPromoCode = window?.localStorage?.getItem('appliedPromoCode')
36
37
 
37
38
  const renderInputField = () => (
39
+ <div className="promo-code-block">
38
40
  <div className="promo-code-block">
39
- <div className="promo-code-block">
40
- <p className="promo-code-text">Promo code</p>
41
- </div>
42
- <input
43
- className="promo-code-input"
44
- placeholder=""
45
- onChange={e => {
46
- setCode(e.target.value)
47
- }}
48
- onKeyPress={event => {
49
- if (event.key === 'Enter' && isPromoCodeHasValue) {
50
- setShowPromoInput(false)
51
- updateTickets(true, 'promo')
52
- }
53
- }}
54
- />
55
- <Button
56
- className="promo-submit-button"
57
- onClick={() => {
58
- if (isPromoCodeHasValue) {
59
- setShowPromoInput(false)
60
- updateTickets(true, 'promo')
61
- }
62
- }}
63
- >
64
- APPLY
65
- </Button>
41
+ <p className="promo-code-text">Promo code</p>
66
42
  </div>
67
- )
43
+ <input
44
+ className="promo-code-input"
45
+ placeholder=""
46
+ onChange={e => {
47
+ setCode(e.target.value)
48
+ }}
49
+ onKeyPress={event => {
50
+ if (event.key === 'Enter' && isPromoCodeHasValue) {
51
+ setShowPromoInput(false)
52
+ updateTickets(true, 'promo')
53
+ }
54
+ }}
55
+ />
56
+ <Button
57
+ className="promo-submit-button"
58
+ onClick={() => {
59
+ if (isPromoCodeHasValue) {
60
+ setShowPromoInput(false)
61
+ updateTickets(true, 'promo')
62
+ }
63
+ }}
64
+ >
65
+ APPLY
66
+ </Button>
67
+ </div>
68
+ )
68
69
 
69
70
  return (
70
71
  <div>
@@ -81,6 +82,11 @@ export const PromoCodeSection = ({
81
82
  <p className="promo-code-success">PROMO CODE APPLIED SUCCESSFULLY</p>
82
83
  </div>
83
84
  ) : null}
85
+ {codeIsApplied ? (
86
+ <div className="alert-info">
87
+ <p className="promo-code-success">PROMO CODE: {appliedPromoCode}</p>
88
+ </div>
89
+ ) : null}
84
90
  {codeIsInvalid ? (
85
91
  <div className="alert-info fail">
86
92
  {showAlertIcons && (
@@ -83,7 +83,7 @@ export const TicketRow = ({
83
83
  // ticketTier.soldOut === false --> means that ticket is in the stock
84
84
  const isSoldOut =
85
85
  ticketTier.sold_out ||
86
- !ticketTier.displayTicket ||
86
+ !(ticketTier.displayTicket || ticketTier.slotGroupId) ||
87
87
  ticketTier.soldOut ||
88
88
  ticketTier.soldOut === false
89
89
 
@@ -1,10 +1,22 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import './style.css'
2
3
 
4
+ import _get from 'lodash/get'
3
5
  import _sortBy from 'lodash/sortBy'
4
- import React, { ReactNode } from 'react'
6
+ import React, { ReactNode, useState } from 'react'
7
+ import { Tooltip } from 'react-tooltip'
5
8
 
9
+ import { FEES_STYLES } from '../../constants'
10
+ import { CONFIGS } from '../../utils'
11
+ import InfoIcon from './InfoIcon'
6
12
  import { TicketRow } from './TicketRow'
7
13
 
14
+ function decodeHTML(html: string): string {
15
+ const textArea = document.createElement('textarea')
16
+ textArea.innerHTML = html
17
+ return textArea.value
18
+ }
19
+
8
20
  interface ITicketsSectionProps {
9
21
  event: any;
10
22
  ticketsList: any;
@@ -17,12 +29,13 @@ interface ITicketsSectionProps {
17
29
  ticketsHeaderComponent?: ReactNode;
18
30
  tableTicketsHeaderComponent?: ReactNode;
19
31
  showGroupNameBlock?: boolean;
20
- currencySybmol?: string;
32
+ currencySymbol?: string;
21
33
  isSeatMapAllowed?: boolean;
34
+ descriptionTrigger?: 'click' | 'hover';
22
35
  }
23
36
 
24
37
  export const TicketsSection = ({
25
- event = { currency: {} },
38
+ event,
26
39
  ticketsList,
27
40
  selectedTickets,
28
41
  handleTicketSelect,
@@ -32,26 +45,37 @@ export const TicketsSection = ({
32
45
  hideTicketsHeader,
33
46
  hideTableTicketsHeader,
34
47
  showGroupNameBlock,
35
- currencySybmol,
48
+ currencySymbol,
36
49
  isSeatMapAllowed,
37
50
  tableTickets,
51
+ descriptionTrigger = 'click',
38
52
  }: ITicketsSectionProps) => {
39
- const {
40
- currency: { symbol },
41
- } = event
53
+ const symbol = _get(event, 'currency.symbol')
42
54
  const sortedTicketsList = sortBySoldOut
43
55
  ? _sortBy(_sortBy(ticketsList, 'sortOrder'), 'soldOut')
44
56
  : _sortBy(ticketsList, 'sortOrder')
45
57
  const showGroup = !!sortedTicketsList.find(ticket => ticket.groupName)
46
- const priceSymbol = currencySybmol ? currencySybmol : symbol
58
+ const priceSymbol = currencySymbol || symbol
59
+
60
+ const [visibleDescription, setVisibleDescription] = useState<string | null>(null)
61
+
62
+ const handleDescriptionToggle = (ticketId: string) => {
63
+ setVisibleDescription(current => (current === ticketId ? null : ticketId))
64
+ }
65
+
47
66
  return (
48
67
  <>
49
68
  {!hideTicketsHeader && ticketsHeaderComponent}
50
69
  {sortedTicketsList.map((ticket, i, arr) => {
51
- const ticketPrice = `${priceSymbol} ${(+ticket.price).toFixed(2)}`
52
- const ticketOldPrice = `${priceSymbol} ${(+ticket.oldPrice).toFixed(2)}`
70
+ const ticketPriceWithoutFees = `${priceSymbol} ${(+ticket.cost).toFixed(2)}`
71
+ const ticketPriceWithFees = `${priceSymbol} ${(+ticket.basePrice).toFixed(2)}`
72
+ const ticketOldPriceWithFees = `${priceSymbol} ${(+ticket.oldBasePrice).toFixed(2)}`
73
+ const ticketOldPriceWithoutFees = `${priceSymbol} ${(+ticket.oldCost).toFixed(2)}`
53
74
 
54
- const isSoldOut = ticket.sold_out || !ticket.displayTicket || ticket.soldOut
75
+ const isSoldOut =
76
+ ticket.sold_out ||
77
+ !(ticket.displayTicket || ticket.slotGroupId) ||
78
+ ticket.soldOut
55
79
  const ticketSelect = (event: any) => {
56
80
  const { value } = event.target
57
81
  handleTicketSelect(ticket.id, value)
@@ -63,11 +87,15 @@ export const TicketsSection = ({
63
87
  }
64
88
 
65
89
  const ticketIsFree = +ticket.price === 0
90
+ const discountTicketPriceElem = CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH || !ticket.feeIncluded ?
91
+ ticketOldPriceWithoutFees : ticketOldPriceWithFees
66
92
  const ticketPriceElem = isSoldOut
67
93
  ? 'SOLD OUT'
68
94
  : ticketIsFree
69
- ? 'FREE'
70
- : ticketPrice
95
+ ? 'FREE'
96
+ : (CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH || !ticket.feeIncluded)
97
+ ? ticketPriceWithoutFees
98
+ : ticketPriceWithFees
71
99
  const isNewGroupTicket = ticket?.groupName !== arr[i - 1]?.groupName
72
100
 
73
101
  return (
@@ -77,17 +105,59 @@ export const TicketsSection = ({
77
105
  {ticket.groupName || ''}
78
106
  </div>
79
107
  ) : null}
80
- <div className={`event-detail__tier ${isSoldOut ? 'disabled' : ''}`}>
108
+ <div
109
+ className={`event-detail__tier ${isSoldOut ? 'disabled' : ''}`}
110
+ id={ticket.id || ticket.name}
111
+ >
81
112
  <div className="event-detail__tier-name">
82
113
  {ticket.displayName || ticket.name}
114
+ {ticket.descriptionRich && (
115
+ <>
116
+ <span
117
+ aria-hidden
118
+ className="info-icon"
119
+ onClick={
120
+ descriptionTrigger === 'click'
121
+ ? () => handleDescriptionToggle(ticket.id)
122
+ : undefined
123
+ }
124
+ onMouseEnter={
125
+ descriptionTrigger === 'hover'
126
+ ? () => setVisibleDescription(ticket.id)
127
+ : undefined
128
+ }
129
+ onMouseLeave={
130
+ descriptionTrigger === 'hover'
131
+ ? () => setVisibleDescription(null)
132
+ : undefined
133
+ }
134
+ data-tooltip-id={`tooltip-${ticket.id}`}
135
+ data-tooltip-content="View ticket info"
136
+ style={{
137
+ marginLeft: 8,
138
+ cursor: 'pointer',
139
+ display: 'flex',
140
+ }}
141
+ >
142
+ <InfoIcon />
143
+ </span>
144
+
145
+ <Tooltip id={`tooltip-${ticket.id}`} place="top">
146
+ {ticket.description || 'No description available'}
147
+ </Tooltip>
148
+ </>
149
+ )}
83
150
  </div>
84
151
  <div className="event-tickets-container">
85
152
  <div className="event-detail__tier-price">
86
- {ticketIsDiscounted && <p className="old-price">{ticketOldPrice}</p>}
153
+ {ticketIsDiscounted && <p className="old-price">{discountTicketPriceElem}</p>}
87
154
  <p className={isSoldOut ? 'sold-out' : ''}>{ticketPriceElem}</p>
88
155
  {!isSoldOut && !ticketIsFree && (
89
156
  <p className="fees">
90
- {ticket.feeIncluded ? '(incl. Fees)' : '(excl. Fees)'}
157
+ {CONFIGS.FEES_STYLE === FEES_STYLES.TRADITIONAL &&
158
+ (ticket.feeIncluded ? '(incl. Fees)' : '(excl. Fees)')}
159
+ {CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH &&
160
+ `(${ticketPriceWithFees} with fees)`}
91
161
  </p>
92
162
  )}
93
163
  </div>
@@ -101,16 +171,30 @@ export const TicketsSection = ({
101
171
  />
102
172
  </div>
103
173
  </div>
174
+ {visibleDescription === ticket.id && (
175
+ <div className="ticket-description">
176
+ <div
177
+ dangerouslySetInnerHTML={{
178
+ __html: decodeHTML(ticket.descriptionRich),
179
+ }}
180
+ />
181
+ </div>
182
+ )}
104
183
  </div>
105
184
  </React.Fragment>
106
185
  )
107
186
  })}
108
187
  {!hideTableTicketsHeader && tableTicketsHeaderComponent}
109
188
  {tableTickets.map((ticket: any, i: any, arr: any) => {
110
- const ticketPrice = `${priceSymbol} ${(+ticket.price).toFixed(2)}`
111
- const ticketOldPrice = `${priceSymbol} ${(+ticket.oldPrice).toFixed(2)}`
189
+ const ticketPriceWithoutFees = `${priceSymbol} ${(+ticket.cost).toFixed(2)}`
190
+ const ticketPriceWithFees = `${priceSymbol} ${(+ticket.basePrice).toFixed(2)}`
191
+ const ticketOldPriceWithFees = `${priceSymbol} ${(+ticket.oldBasePrice).toFixed(2)}`
192
+ const ticketOldPriceWithoutFees = `${priceSymbol} ${(+ticket.oldCost).toFixed(2)}`
112
193
 
113
- const isSoldOut = ticket.sold_out || !ticket.displayTicket || ticket.soldOut
194
+ const isSoldOut =
195
+ ticket.sold_out ||
196
+ !(ticket.displayTicket || ticket.slotGroupId) ||
197
+ ticket.soldOut
114
198
  const ticketSelect = (event: any) => {
115
199
  const { value } = event.target
116
200
  handleTicketSelect(ticket.id, value, true)
@@ -122,53 +206,101 @@ export const TicketsSection = ({
122
206
  }
123
207
 
124
208
  const ticketIsFree = +ticket.price === 0
209
+ const discountTicketPriceElem = CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH || !ticket.feeIncluded ?
210
+ ticketOldPriceWithoutFees : ticketOldPriceWithFees
125
211
  const ticketPriceElem = isSoldOut
126
212
  ? 'SOLD OUT'
127
213
  : ticketIsFree
128
- ? 'FREE'
129
- : ticketPrice
214
+ ? 'FREE'
215
+ : (CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH || !ticket.feeIncluded)
216
+ ? ticketPriceWithoutFees
217
+ : ticketPriceWithFees
130
218
  const isNewGroupTicket = ticket?.groupName !== arr[i - 1]?.groupName
131
219
 
132
220
  return (
133
- <>
134
- <React.Fragment key={ticket.id || ticket.name}>
135
- {showGroupNameBlock && showGroup && isNewGroupTicket ? (
136
- <div className="event-detail__tier group-title">
137
- {ticket.groupName || ''}
138
- </div>
139
- ) : null}
140
- <div className={`event-detail__tier ${isSoldOut ? 'disabled' : ''}`}>
141
- <div className="event-detail__tier-name">
142
- {ticket.displayName || ticket.name}
221
+ <React.Fragment key={ticket.id || ticket.name}>
222
+ {showGroupNameBlock && showGroup && isNewGroupTicket ? (
223
+ <div className="event-detail__tier group-title">
224
+ {ticket.groupName || ''}
225
+ </div>
226
+ ) : null}
227
+ <div
228
+ className={`event-detail__tier ${isSoldOut ? 'disabled' : ''}`}
229
+ id={ticket.id || ticket.name}
230
+ >
231
+ <div className="event-detail__tier-name">
232
+ {ticket.displayName || ticket.name}
233
+ {ticket.descriptionRich && (
234
+ <>
235
+ <span
236
+ aria-hidden
237
+ className="info-icon"
238
+ onClick={
239
+ descriptionTrigger === 'click'
240
+ ? () => handleDescriptionToggle(ticket.id)
241
+ : undefined
242
+ }
243
+ onMouseEnter={
244
+ descriptionTrigger === 'hover'
245
+ ? () => setVisibleDescription(ticket.id)
246
+ : undefined
247
+ }
248
+ onMouseLeave={
249
+ descriptionTrigger === 'hover'
250
+ ? () => setVisibleDescription(null)
251
+ : undefined
252
+ }
253
+ style={{ marginLeft: 8, cursor: 'pointer', display: 'flex' }}
254
+ data-tooltip-id={`tooltip-${ticket.id}`}
255
+ data-tooltip-content="View ticket info"
256
+ >
257
+ <InfoIcon />
258
+ </span>
259
+ <Tooltip id={`tooltip-${ticket.id}`} place="top">
260
+ {ticket.description || 'No description available'}
261
+ </Tooltip>
262
+ </>
263
+ )}
264
+ </div>
265
+ <div className="event-tickets-container">
266
+ <div className="event-detail__tier-price">
267
+ {ticketIsDiscounted && <p className="old-price">{discountTicketPriceElem}</p>}
268
+ <p className={isSoldOut ? 'sold-out' : ''}>{ticketPriceElem}</p>
269
+ {!isSoldOut && !ticketIsFree && (
270
+ <p className="fees">
271
+ {CONFIGS.FEES_STYLE === FEES_STYLES.TRADITIONAL &&
272
+ (ticket.feeIncluded ? '(incl. Fees)' : '(excl. Fees)')}
273
+ {CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH &&
274
+ `(${ticketPriceWithFees} with fees)`}
275
+ </p>
276
+ )}
277
+ {ticket.depositPercent && (
278
+ <p className="deposits">{ticket.depositPercent + '% DEPOSIT'}</p>
279
+ )}
143
280
  </div>
144
- <div className="event-tickets-container">
145
- <div className="event-detail__tier-price">
146
- {ticketIsDiscounted && <p className="old-price">{ticketOldPrice}</p>}
147
- <p className={isSoldOut ? 'sold-out' : ''}>{ticketPriceElem}</p>
148
- {!isSoldOut && !ticketIsFree && (
149
- <p className="fees">
150
- {ticket.feeIncluded ? '(incl. Fees)' : '(excl. Fees)'}
151
- </p>
152
- )}
153
- {ticket.depositPercent && (
154
- <p className="deposits">{ticket.depositPercent + '% DEPOSIT'}</p>
155
- )}
156
- </div>
157
- <div className="event-detail__tier-state" style={{ minWidth: 55 }}>
158
- <TicketRow
159
- tableType={true}
160
- ticketTier={ticket}
161
- prevTicketTier={arr[i - 1]}
162
- selectedTickets={selectedTickets}
163
- handleTicketSelect={ticketSelect}
164
- isSeatMapAllowed={isSeatMapAllowed}
165
- event={event}
166
- />
167
- </div>
281
+ <div className="event-detail__tier-state" style={{ minWidth: 55 }}>
282
+ <TicketRow
283
+ tableType={true}
284
+ ticketTier={ticket}
285
+ prevTicketTier={arr[i - 1]}
286
+ selectedTickets={selectedTickets}
287
+ handleTicketSelect={ticketSelect}
288
+ isSeatMapAllowed={isSeatMapAllowed}
289
+ event={event}
290
+ />
168
291
  </div>
169
292
  </div>
170
- </React.Fragment>
171
- </>
293
+ {visibleDescription === ticket.id && (
294
+ <div className="ticket-description">
295
+ <div
296
+ dangerouslySetInnerHTML={{
297
+ __html: decodeHTML(ticket.descriptionRich),
298
+ }}
299
+ />
300
+ </div>
301
+ )}
302
+ </div>
303
+ </React.Fragment>
172
304
  )
173
305
  })}
174
306
  </>