tf-checkout-react 1.6.6-beta.3 → 1.6.6-beta.31

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 (110) hide show
  1. package/README.md +61 -40
  2. package/dist/adapters/customFields.d.ts +1 -0
  3. package/dist/api/checkout.d.ts +1 -0
  4. package/dist/api/common.d.ts +1 -0
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api/preRegistrationComplete.d.ts +1 -1
  7. package/dist/components/addonsContainer/AddonComponent.d.ts +5 -1
  8. package/dist/components/addonsContainer/SimpleAddonsContainer.d.ts +17 -0
  9. package/dist/components/addonsContainer/index.d.ts +5 -1
  10. package/dist/components/billing-info-container/index.d.ts +8 -1
  11. package/dist/components/billing-info-container/utils.d.ts +25 -1
  12. package/dist/components/common/DatePickerField.d.ts +7 -1
  13. package/dist/components/countdown/index.d.ts +1 -1
  14. package/dist/components/forgotPasswordModal/index.d.ts +2 -1
  15. package/dist/components/myTicketsContainer/index.d.ts +3 -2
  16. package/dist/components/orderDetailsContainer/index.d.ts +7 -1
  17. package/dist/components/paymentContainer/OrderDetails.d.ts +8 -0
  18. package/dist/components/paymentContainer/handlePayment.d.ts +15 -0
  19. package/dist/components/paymentContainer/index.d.ts +10 -5
  20. package/dist/components/preRegistration/FieldsSection.d.ts +7 -1
  21. package/dist/components/preRegistration/PreRegistrationComplete.d.ts +6 -0
  22. package/dist/components/preRegistration/constants.d.ts +2 -2
  23. package/dist/components/preRegistration/index.d.ts +6 -0
  24. package/dist/components/resetPasswordContainer/index.d.ts +2 -2
  25. package/dist/components/stripePayment/index.d.ts +18 -3
  26. package/dist/components/ticketsContainer/InfoIcon.d.ts +5 -0
  27. package/dist/components/ticketsContainer/TicketsSection.d.ts +3 -2
  28. package/dist/components/ticketsContainer/TimeSlotsSection.d.ts +25 -0
  29. package/dist/components/ticketsContainer/index.d.ts +18 -5
  30. package/dist/components/timerWidget/index.d.ts +2 -1
  31. package/dist/constants/index.d.ts +5 -0
  32. package/dist/index.d.ts +4 -1
  33. package/dist/tf-checkout-react.cjs.development.js +5196 -3599
  34. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  35. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  36. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  37. package/dist/tf-checkout-react.esm.js +5199 -3605
  38. package/dist/tf-checkout-react.esm.js.map +1 -1
  39. package/dist/tf-checkout-styles.css +1 -1
  40. package/dist/types/add_on.d.ts +1 -0
  41. package/dist/types/checkoutPageConfigs.d.ts +1 -1
  42. package/dist/types/order-data.d.ts +2 -1
  43. package/dist/utils/auth.d.ts +8 -0
  44. package/dist/utils/customFields.d.ts +11 -0
  45. package/dist/utils/getDomain.d.ts +1 -1
  46. package/dist/utils/index.d.ts +1 -1
  47. package/dist/utils/setConfigs.d.ts +1 -0
  48. package/package.json +3 -2
  49. package/src/adapters/customFields.ts +7 -1
  50. package/src/api/auth.ts +2 -1
  51. package/src/api/checkout.ts +8 -4
  52. package/src/api/common.ts +49 -2
  53. package/src/api/interceptors.ts +7 -23
  54. package/src/api/preRegistrationComplete.ts +1 -1
  55. package/src/api/publicRequest.ts +10 -0
  56. package/src/components/addonsContainer/AddonComponent.tsx +71 -11
  57. package/src/components/addonsContainer/SimpleAddonsContainer.tsx +388 -0
  58. package/src/components/addonsContainer/index.tsx +189 -58
  59. package/src/components/billing-info-container/index.tsx +704 -390
  60. package/src/components/billing-info-container/{utils.ts → utils.tsx} +119 -0
  61. package/src/components/common/CheckboxField/index.tsx +1 -1
  62. package/src/components/common/DatePickerField.tsx +25 -10
  63. package/src/components/common/SnackbarAlert.tsx +32 -34
  64. package/src/components/confirmationContainer/index.tsx +1 -1
  65. package/src/components/countdown/index.tsx +22 -22
  66. package/src/components/delegationsContainer/IssueComponent.tsx +2 -1
  67. package/src/components/forgotPasswordModal/index.tsx +44 -13
  68. package/src/components/loginForm/index.tsx +1 -1
  69. package/src/components/loginModal/index.tsx +2 -2
  70. package/src/components/loginModal/style.css +3 -1
  71. package/src/components/myTicketsContainer/index.tsx +13 -9
  72. package/src/components/orderDetailsContainer/index.tsx +188 -173
  73. package/src/components/paymentContainer/OrderDetails.tsx +170 -0
  74. package/src/components/paymentContainer/handlePayment.ts +86 -0
  75. package/src/components/paymentContainer/index.tsx +253 -226
  76. package/src/components/paymentContainer/style.css +113 -0
  77. package/src/components/preRegistration/FieldsSection.tsx +8 -0
  78. package/src/components/preRegistration/PreRegistrationComplete.tsx +128 -118
  79. package/src/components/preRegistration/PreRegistrationInformations.tsx +21 -15
  80. package/src/components/preRegistration/constants.tsx +10 -4
  81. package/src/components/preRegistration/index.tsx +194 -174
  82. package/src/components/registerForm/constants.tsx +3 -1
  83. package/src/components/registerForm/index.tsx +3 -3
  84. package/src/components/resetPasswordContainer/index.tsx +12 -13
  85. package/src/components/seatMapContainer/TicketsSection.tsx +2 -2
  86. package/src/components/stripePayment/index.tsx +129 -24
  87. package/src/components/ticketsContainer/InfoIcon.tsx +35 -0
  88. package/src/components/ticketsContainer/PromoCodeSection.tsx +34 -28
  89. package/src/components/ticketsContainer/TicketRow.tsx +1 -1
  90. package/src/components/ticketsContainer/TicketsSection.tsx +178 -52
  91. package/src/components/ticketsContainer/TimeSlotsSection.tsx +120 -0
  92. package/src/components/ticketsContainer/index.tsx +209 -99
  93. package/src/components/timerWidget/index.tsx +15 -3
  94. package/src/constants/index.ts +2 -0
  95. package/src/hoc/CustomFields/index.tsx +9 -1
  96. package/src/index.ts +7 -2
  97. package/src/types/add_on.ts +1 -0
  98. package/src/types/api/common.d.ts +27 -0
  99. package/src/types/api/orders.d.ts +19 -3
  100. package/src/types/api/payment.d.ts +5 -2
  101. package/src/types/api/preRegistrationComplete.d.ts +2 -2
  102. package/src/types/checkoutPageConfigs.ts +1 -1
  103. package/src/types/order-data.ts +2 -1
  104. package/src/types/pre-registration-complete.d.ts +6 -1
  105. package/src/utils/auth.ts +32 -0
  106. package/src/utils/cookies.ts +42 -11
  107. package/src/utils/customFields.ts +22 -0
  108. package/src/utils/getDomain.ts +10 -4
  109. package/src/utils/index.ts +1 -1
  110. package/src/utils/setConfigs.ts +3 -1
@@ -1,17 +1,20 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import './style.css'
2
3
 
3
- import { createTheme, ThemeOptions } from '@mui/material'
4
+ import { ThemeOptions } from '@mui/material'
4
5
  import Alert from '@mui/material/Alert'
5
- import { ThemeProvider } from '@mui/private-theming'
6
+ import { createTheme, ThemeProvider } from '@mui/material/styles'
6
7
  import { CSSProperties } from '@mui/styles'
7
8
  import axios, { AxiosError } from 'axios'
8
9
  import _filter from 'lodash/filter'
9
10
  import _find from 'lodash/find'
11
+ import _flatten from 'lodash/flatten'
10
12
  import _forEach from 'lodash/forEach'
11
13
  import _get from 'lodash/get'
12
14
  import _identity from 'lodash/identity'
13
15
  import _includes from 'lodash/includes'
14
16
  import _isEmpty from 'lodash/isEmpty'
17
+ import _map from 'lodash/map'
15
18
  import _some from 'lodash/some'
16
19
  import moment from 'moment-timezone'
17
20
  import React, { ReactNode, useEffect, useRef, useState } from 'react'
@@ -23,22 +26,22 @@ import {
23
26
  getEvent,
24
27
  getProfileData,
25
28
  getTickets,
26
- logout,
27
29
  postOnCheckout,
28
30
  } from '../../api'
29
- import { getPreRegistrationInfluancers } from '../../api/preRegistrationComplete'
31
+ import { getTimeSlotsByDate } from '../../api/common'
32
+ import { getPreRegistrationInfluencers } from '../../api/preRegistrationComplete'
30
33
  import { X_TF_ECOMMERCE } from '../../constants'
31
34
  import { useCookieListener } from '../../hooks/useCookieListener'
32
35
  import { useLocalStorageListener } from '../../hooks/useLocalStorageListener'
33
36
  import { usePixel } from '../../hooks/usePixel'
34
37
  import {
35
38
  createCheckoutDataBodyWithDefaultHolder,
36
- deleteCookieByName,
37
39
  getCookieByName,
38
40
  getQueryVariable,
39
41
  isBrowser,
40
42
  setLoggedUserData,
41
43
  } from '../../utils'
44
+ import { logoutUser } from '../../utils/auth'
42
45
  import { Loader } from '../common/index'
43
46
  import { PoweredBy } from '../common/PoweredBy'
44
47
  import ConfirmModal from '../confirmModal'
@@ -50,6 +53,24 @@ import { AccessCodeSection } from './AccessCodeSection'
50
53
  import { PromoCodeSection } from './PromoCodeSection'
51
54
  import { ReferralLogic } from './ReferralLogic'
52
55
  import { TicketsSection } from './TicketsSection'
56
+ import TimeSlotsSection from './TimeSlotsSection'
57
+
58
+ const checkUserPreregistration = (preregisteredUsers: IInfluencerData[]) => {
59
+ const isWindowDefined = typeof window !== 'undefined'
60
+ const userDataString = isWindowDefined ? window.localStorage.getItem('user_data') : ''
61
+ let isPreregisteredUser = false
62
+ if (userDataString) {
63
+ const user = JSON.parse(userDataString)
64
+ isPreregisteredUser = _some(preregisteredUsers, item => item.email === user?.email)
65
+ }
66
+ return isPreregisteredUser
67
+ }
68
+
69
+ interface TimeSlotsAttributes {
70
+ ValidPromoCode: boolean;
71
+ is_access_code: boolean;
72
+ isPromotionsEnabled: boolean;
73
+ }
53
74
 
54
75
  interface CartSuccess {
55
76
  skip_billing_page: boolean;
@@ -57,6 +78,7 @@ interface CartSuccess {
57
78
  hash?: string | number;
58
79
  total?: string | number;
59
80
  hasAddOn?: boolean;
81
+ eventSlug?: string;
60
82
  }
61
83
 
62
84
  export interface IGetTickets {
@@ -65,12 +87,16 @@ export interface IGetTickets {
65
87
  getTicketsLabel?: string;
66
88
  contentStyle?: React.CSSProperties;
67
89
  onAddToCartError: (e: AxiosError) => void;
68
- onGetTicketsSuccess: (response: any) => void;
90
+ onGetTicketsSuccess: (response: {
91
+ data: { attributes: { tickets: ITicketData[] } };
92
+ }) => void;
69
93
  onGetTicketsPress: () => void;
70
94
  onGetTicketsError: (e: AxiosError) => void;
71
95
  onLogoutSuccess: () => void;
72
96
  onLogoutError: (e: AxiosError) => void;
73
- onGetProfileDataSuccess: (response: any) => void;
97
+ onGetProfileDataSuccess: (response: {
98
+ data: { attributes: { [key: string]: string | number | boolean | null } };
99
+ }) => void;
74
100
  onGetProfileDataError: (e: AxiosError) => void;
75
101
  onLoginSuccess: () => void;
76
102
  handleNotInvitedModalClose: () => void;
@@ -90,7 +116,7 @@ export interface IGetTickets {
90
116
  sortBySoldOut?: boolean;
91
117
  disableCountdownLeadingZero?: boolean;
92
118
  isLoggedIn?: boolean;
93
- actionsSectionComponent?: any;
119
+ actionsSectionComponent?: ReactNode;
94
120
  ticketsHeaderComponent?: ReactNode;
95
121
  hideTicketsHeader?: boolean;
96
122
  tableTicketsHeaderComponent?: ReactNode;
@@ -101,7 +127,7 @@ export interface IGetTickets {
101
127
  showPoweredByImage?: boolean;
102
128
  promoText?: string;
103
129
  showGroupNameBlock?: boolean;
104
- currencySybmol?: string;
130
+ currencySymbol?: string;
105
131
  onReserveButtonClick?: () => void;
106
132
  onPendingVerification?: () => void;
107
133
  showAlertIcons?: boolean;
@@ -159,13 +185,12 @@ export const TicketsContainer = ({
159
185
  showPoweredByImage = false,
160
186
  promoText,
161
187
  showGroupNameBlock = false,
162
- currencySybmol = '',
188
+ currencySymbol = '',
163
189
  onReserveButtonClick = _identity,
164
190
  onPendingVerification = _identity,
165
191
  showAlertIcons = true,
166
192
  }: IGetTickets) => {
167
193
  const [selectedTickets, setSelectedTickets] = useState({} as ISelectedTickets)
168
- const isWindowDefined = typeof window !== 'undefined'
169
194
  const [isLogged, setIsLogged] = useState(Boolean(getCookieByName(X_TF_ECOMMERCE)))
170
195
  const [showLoginModal, setShowLoginModal] = useState(false)
171
196
  const [tickets, setTickets] = useState([] as ITicketData[])
@@ -180,19 +205,31 @@ export const TicketsContainer = ({
180
205
  const [codeIsInvalid, setCodeIsInvalid] = useState(false)
181
206
  const [showAccessCodeSection, setShowAccessCodeSection] = useState(isAccessCodeEnabled)
182
207
  const [showPromoCodeSection, setShowPromoCodeSection] = useState(isPromotionsEnabled)
183
- const [error, setError] = useState(null)
208
+ const [error, setError] = useState<string | null>(null)
184
209
  const [isNotInvitedError, setIsNotInvitedError] = useState('')
185
210
  const [isInvalidLinkError, setIsInvalidLinkError] = useState('')
186
211
  const [pendingVerificationMessage, setPendingVerificationMessage] = useState()
187
- const [preregistereds, setPreregistereds] = useState([] as Array<IInfluancerData>)
212
+ const [preregisteredUsers, setPreregisteredUsers] = useState(
213
+ [] as Array<IInfluencerData>
214
+ )
188
215
  const [ticketsNotAvailableModalOpen, setTicketsNotAvailableModalOpen] = useState(false)
189
- const [isPreregistred, setIsPreregisred] = useState(checkUserPreregistration())
216
+ const [isPreregistered, setIsPreregistered] = useState(
217
+ checkUserPreregistration(preregisteredUsers)
218
+ )
190
219
  const ticketsContainerRef = useRef<HTMLDivElement>(null)
191
220
  const pageUrl = isBrowser ? window.location.href.split('?')[0] : ''
221
+ const isTimeSlotEvent = event?.isTimeSlotEvent
222
+ const [availableDates, setAvailableDates] = useState<string[]>([])
223
+ const [selectedDate, setSelectedDate] = useState<string | null>(null)
224
+ const [timeSlotGroups, setTimeSlotGroups] = useState<{ [key: string]: any[] }>({})
225
+
192
226
  useCookieListener(X_TF_ECOMMERCE, value => setIsLogged(Boolean(value)))
193
227
  useLocalStorageListener('user_data', (user: any) => {
194
- const isPreregistredUser = _some(preregistereds, item => item.email === user?.email)
195
- setIsPreregisred(isPreregistredUser)
228
+ const isPreregisteredUser = _some(
229
+ preregisteredUsers,
230
+ item => item.email === user?.email
231
+ )
232
+ setIsPreregistered(isPreregisteredUser)
196
233
  })
197
234
  usePixel(eventId, { pageUrl })
198
235
 
@@ -203,20 +240,20 @@ export const TicketsContainer = ({
203
240
  useEffect(() => {
204
241
  if (eventId) {
205
242
  getTicketsApi()
206
- fetchPreregisteredsData()
243
+ fetchPreregisteredData()
207
244
  }
208
- }, [eventId])
245
+ }, [eventId, isTimeSlotEvent])
209
246
 
210
247
  useEffect(() => {
211
- setIsPreregisred(checkUserPreregistration())
212
- }, [preregistereds])
248
+ setIsPreregistered(checkUserPreregistration(preregisteredUsers))
249
+ }, [preregisteredUsers])
213
250
 
214
251
  useEffect(() => {
215
252
  if (isLogged) {
216
253
  fetchUserData()
217
254
  .then(res => {
218
255
  window.localStorage.setItem('user_data', JSON.stringify(res))
219
- onGetProfileDataSuccess(res)
256
+ onGetProfileDataSuccess({ data: { attributes: res } })
220
257
  })
221
258
  .catch(e => {
222
259
  if (axios.isAxiosError(e)) {
@@ -226,20 +263,8 @@ export const TicketsContainer = ({
226
263
  }
227
264
  }, [isLogged])
228
265
 
229
- const handleLogout = async () => {
230
- try {
231
- await logout()
232
- onLogoutSuccess()
233
- if (isBrowser) {
234
- window.localStorage.removeItem('user_data')
235
- setIsLogged(false)
236
- const event = new window.CustomEvent('tf-logout')
237
- deleteCookieByName('X-TF-ECOMMERCE')
238
- window.document.dispatchEvent(event)
239
- }
240
- } catch (e) {
241
- onLogoutError(e)
242
- }
266
+ const handleLogout = () => {
267
+ logoutUser({ onLogoutError, onLogoutSuccess, setIsLogged })
243
268
  }
244
269
 
245
270
  const handleExternalLogin = () => {
@@ -258,23 +283,58 @@ export const TicketsContainer = ({
258
283
  }
259
284
  }
260
285
 
261
- async function getTicketsApi(isUpdateingCode?: boolean, type?: string) {
286
+ const getTimeSlots = async (date: string | null, type?: string): Promise<void> => {
287
+ if (!date) {
288
+ setShowPromoCodeSection(false)
289
+ return
290
+ }
291
+
262
292
  try {
263
- isUpdateingCode ? setCodeIsLoading(true) : setIsLoading(true)
264
- const previewKey = getQueryVariable('pk') || undefined
265
- const response = await getTickets(eventId, code, previewKey)
266
- const eventResponse = await getEvent(eventId, previewKey)
267
- if (response.success) {
268
- const attributes = response?.data?.attributes
269
- type === 'promo' && setCodeIsApplied(attributes.ValidPromoCode)
270
- type === 'promo' && setCodeIsInvalid(!attributes.ValidPromoCode)
271
- setTickets(attributes.tickets)
272
- setShowWaitingList(attributes.showWaitingList)
273
- onGetTicketsSuccess(response.data)
293
+ const timeSlotsResponse = await getTimeSlotsByDate(
294
+ eventId,
295
+ moment(date).format('YYYY-MM-DD'),
296
+ code
297
+ )
298
+
299
+ const groupedTickets: any = {}
300
+ _map(timeSlotsResponse.data.attributes.timeslots, (timeslots, date) => {
301
+ const dateKeyChunks = date.split('-')
302
+ const dateKey = [dateKeyChunks[0], dateKeyChunks[1], dateKeyChunks[2]].join('-')
303
+
304
+ _forEach(timeslots, ticketList => {
305
+ if (!groupedTickets[dateKey]) {
306
+ groupedTickets[dateKey] = []
307
+ }
308
+ groupedTickets[dateKey] = groupedTickets[dateKey].concat(ticketList)
309
+ })
310
+ })
311
+
312
+ setTimeSlotGroups(groupedTickets)
313
+ setShowPromoCodeSection(true)
314
+
315
+ const attributes: TimeSlotsAttributes = timeSlotsResponse?.data?.attributes
316
+
317
+ if (type === 'promo') {
318
+ setCodeIsApplied(attributes.ValidPromoCode)
319
+ setCodeIsInvalid(!attributes.ValidPromoCode)
274
320
  setCode('')
321
+ window.localStorage.setItem('appliedPromoCode', code)
275
322
  setShowAccessCodeSection(attributes.is_access_code)
276
323
  setShowPromoCodeSection(attributes.isPromotionsEnabled)
277
324
  }
325
+ } catch (error) {}
326
+ }
327
+
328
+ async function getTicketsApi(isUpdatingCode?: boolean, type?: string) {
329
+ try {
330
+ if (isUpdatingCode) {
331
+ setCodeIsLoading(true)
332
+ } else {
333
+ setIsLoading(true)
334
+ }
335
+
336
+ const previewKey = getQueryVariable('pk') || undefined
337
+ const eventResponse = await getEvent(eventId, previewKey)
278
338
  if (eventResponse.success) {
279
339
  const event = eventResponse.data.attributes
280
340
  setEvent(event)
@@ -282,11 +342,36 @@ export const TicketsContainer = ({
282
342
  if (event.country && isBrowser) {
283
343
  window.localStorage.setItem('eventCountry', event.country)
284
344
  }
345
+
346
+ if (event.isTimeSlotEvent) {
347
+ getTimeSlots(selectedDate, code)
348
+ // For timeslots we shouldn't fetch all tickets at once
349
+ setAvailableDates(event?.timeslotSettings?.availableDates ?? [])
350
+ return
351
+ }
352
+ }
353
+
354
+ const response = await getTickets(eventId, code, previewKey)
355
+
356
+ if (response.success) {
357
+ const attributes = response?.data?.attributes
358
+ type === 'promo' && setCodeIsApplied(attributes.ValidPromoCode)
359
+ type === 'promo' && setCodeIsInvalid(!attributes.ValidPromoCode)
360
+ setTickets(attributes.tickets)
361
+ setShowWaitingList(attributes.showWaitingList)
362
+ onGetTicketsSuccess({
363
+ data: { attributes: { tickets: response.data.attributes.tickets } },
364
+ })
365
+ setCode('')
366
+ window.localStorage.setItem('appliedPromoCode', code)
367
+ setShowAccessCodeSection(attributes.is_access_code)
368
+ setShowPromoCodeSection(attributes.isPromotionsEnabled)
285
369
  }
286
370
  } catch (e) {
287
371
  if (axios.isAxiosError(e)) {
288
372
  onGetTicketsError(e)
289
- setError(_get(e, 'response.data.message'))
373
+ setError(_get(e, 'response.data.message', ''))
374
+ window.localStorage.removeItem('appliedPromoCode')
290
375
  }
291
376
  } finally {
292
377
  setIsLoading(false)
@@ -294,15 +379,15 @@ export const TicketsContainer = ({
294
379
  }
295
380
  }
296
381
 
297
- async function fetchPreregisteredsData() {
382
+ async function fetchPreregisteredData() {
298
383
  try {
299
- const preregistrationData = await getPreRegistrationInfluancers({
384
+ const preregistrationData = await getPreRegistrationInfluencers({
300
385
  eventId,
301
386
  })
302
- setPreregistereds(preregistrationData.data.attributes.influencers)
387
+ setPreregisteredUsers(preregistrationData.data.attributes.influencers)
303
388
  } catch (error) {
304
389
  if (axios.isAxiosError(error)) {
305
- setError(_get(error, 'response.data.message'))
390
+ setError(_get(error, 'response.data.message', ''))
306
391
  }
307
392
  }
308
393
  }
@@ -320,16 +405,6 @@ export const TicketsContainer = ({
320
405
  })
321
406
  }
322
407
 
323
- function checkUserPreregistration() {
324
- const userDataString = isWindowDefined ? window.localStorage.getItem('user_data') : ''
325
- let isPreregistredUser = false
326
- if (userDataString) {
327
- const user = JSON.parse(userDataString)
328
- isPreregistredUser = _some(preregistereds, item => item.email === user?.email)
329
- }
330
- return isPreregistredUser
331
- }
332
-
333
408
  const handleOrdersClick = () => {
334
409
  if (isBrowser) {
335
410
  window.location.href = ordersPath ?? '/orders'
@@ -341,8 +416,13 @@ export const TicketsContainer = ({
341
416
  }
342
417
 
343
418
  const handleBook = async () => {
419
+ const timeSlotTickets = _flatten(_map(timeSlotGroups, slots => slots))
420
+
344
421
  setHandleBookIsLoading(true)
345
- const ticket = _find(tickets, item => selectedTickets[item.id] > 0) || ({} as ITicket)
422
+ const ticket = event?.isTimeSlotEvent
423
+ ? _find(timeSlotTickets || [], item => Number(selectedTickets[item.id]) > 0) ||
424
+ ({} as ITicket)
425
+ : _find(tickets, item => Number(selectedTickets[item.id]) > 0) || ({} as ITicket)
346
426
  const optionName = _get(ticket, 'optionName')
347
427
  const ticketId = _get(ticket, 'id')
348
428
  const isTableType = _get(ticket, 'isTable', false)
@@ -375,12 +455,13 @@ export const TicketsContainer = ({
375
455
  const pageConfigsDataResponse = enableAddOns
376
456
  ? await getCheckoutPageConfigs()
377
457
  : {
378
- status: 200,
379
- data: { attributes: _get(result, 'data.attributes') },
380
- }
458
+ status: 200,
459
+ data: { attributes: _get(result, 'data.attributes') },
460
+ }
381
461
 
382
- if (result.status === 200 && pageConfigsDataResponse.status === 200) {
383
- const pageConfigsData = _get(pageConfigsDataResponse, 'data.attributes') || {}
462
+ if (pageConfigsDataResponse.status === 200) {
463
+ const pageConfigsData: any =
464
+ _get(pageConfigsDataResponse, 'data.attributes') || {}
384
465
 
385
466
  const skipBillingPage = pageConfigsData.skip_billing_page ?? false
386
467
  const hasAddOn = pageConfigsData.has_add_on ?? false
@@ -395,7 +476,7 @@ export const TicketsContainer = ({
395
476
  // Get user data for checkout data
396
477
  const userData =
397
478
  isBrowser && window.localStorage.getItem('user_data')
398
- ? JSON.parse(window.localStorage.getItem('user_data') || '')
479
+ ? JSON.parse(window.localStorage.getItem('user_data') || '{}')
399
480
  : {}
400
481
 
401
482
  const checkoutBody = createCheckoutDataBodyWithDefaultHolder(
@@ -417,11 +498,13 @@ export const TicketsContainer = ({
417
498
  hash,
418
499
  total,
419
500
  hasAddOn,
501
+ eventSlug: event?.slug,
420
502
  })
421
503
  }
422
504
  } catch (e) {
423
- if (e.response?.data?.data?.hasUnverifiedOrder) {
424
- setPendingVerificationMessage(e.response?.data?.message)
505
+ const errorResponse: any = _get(e, 'response', {})
506
+ if (errorResponse?.data?.data?.hasUnverifiedOrder) {
507
+ setPendingVerificationMessage(errorResponse?.data?.message)
425
508
  } else if (axios.isAxiosError(e)) {
426
509
  onAddToCartError(e)
427
510
  const message = _get(e, 'response.data.message', '')
@@ -447,8 +530,8 @@ export const TicketsContainer = ({
447
530
  }
448
531
  }
449
532
 
450
- const updateTickets = (isUpdateingCode?: boolean, type?: string) => {
451
- getTicketsApi(isUpdateingCode, type)
533
+ const updateTickets = (isUpdatingCode?: boolean, type?: string) => {
534
+ getTicketsApi(isUpdatingCode, type)
452
535
  }
453
536
 
454
537
  const fetchUserData = async () => {
@@ -458,11 +541,15 @@ export const TicketsContainer = ({
458
541
  return profileDataObj
459
542
  }
460
543
 
461
- const isTicketOnSale = _some(
462
- tickets,
463
- item =>
464
- (item.salesStarted || event?.presalesStarted) && !item.salesEnded && !item.soldOut
465
- )
544
+ const isTicketOnSale = event?.isTimeSlotEvent
545
+ ? true
546
+ : _some(
547
+ tickets,
548
+ item =>
549
+ (item.salesStarted || event?.presalesStarted) &&
550
+ !item.salesEnded &&
551
+ !item.soldOut
552
+ )
466
553
 
467
554
  const eventHasTickets = !_isEmpty(tickets)
468
555
  const isSalesClosed = event?.salesEnded
@@ -483,7 +570,7 @@ export const TicketsContainer = ({
483
570
  if (
484
571
  !handleBookIsLoading &&
485
572
  !_isEmpty(selectedTickets) &&
486
- Object.values(selectedTickets)[0] > 0
573
+ Number(Object.values(selectedTickets)[0]) > 0
487
574
  ) {
488
575
  handleBook()
489
576
  } else {
@@ -519,10 +606,10 @@ export const TicketsContainer = ({
519
606
 
520
607
  const wrappedActionsSectionComponent = React.isValidElement(ActionsSectionComponent)
521
608
  ? React.cloneElement(ActionsSectionComponent as React.ReactElement<any>, {
522
- handleGetTicketClick,
523
- isTicketOnSale,
524
- isTicketAvailable,
525
- })
609
+ handleGetTicketClick,
610
+ isTicketOnSale,
611
+ isTicketAvailable,
612
+ })
526
613
  : null
527
614
 
528
615
  const externalUrl = event?.redirectUrl
@@ -564,16 +651,17 @@ export const TicketsContainer = ({
564
651
  )}. \n If you pre-registered, log into your account first to buy your pre-sale ticket.`
565
652
 
566
653
  const tableTickets = _filter(tickets, (ticket: any) => ticket.isTable)
567
- const ordinarTickets = {} as ITicket
654
+ const ordinaryTickets = {} as ITicket
568
655
  _forEach(tickets, (ticket: any, key: string) => {
569
656
  if (!ticket.isTable) {
570
- ordinarTickets[key] = ticket
657
+ ordinaryTickets[key] = ticket
571
658
  }
572
659
  })
660
+
573
661
  return (
574
662
  <ThemeProvider theme={themeMui}>
575
663
  {!isLoading && <ReferralLogic eventId={eventId} />}
576
- <div className={`get-tickets-page ${theme}`} style={contentStyle}>
664
+ <div className={`get-tickets-page ${theme} open-ground`} style={contentStyle}>
577
665
  {isInvalidLinkError && (
578
666
  <ConfirmModal
579
667
  message={isInvalidLinkError}
@@ -588,7 +676,7 @@ export const TicketsContainer = ({
588
676
  message={isNotInvitedError}
589
677
  onClose={() => onClose('notInvited')}
590
678
  onConfirm={() => onClose('notInvited')}
591
- // loading={removeFromResaleLoading}
679
+ // loading={removeFromResaleLoading}
592
680
  />
593
681
  )}
594
682
  {error && (
@@ -612,11 +700,31 @@ export const TicketsContainer = ({
612
700
  {isLoading ? (
613
701
  <Loader />
614
702
  ) : (
615
- <div ref={ticketsContainerRef}>
616
- {!isSalesClosed && (
703
+ <div ref={ticketsContainerRef} className="tickets-section-container">
704
+ {isTimeSlotEvent && (
705
+ <TimeSlotsSection
706
+ event={event}
707
+ eventId={eventId}
708
+ availableDates={availableDates}
709
+ selectedDate={selectedDate}
710
+ setSelectedDate={setSelectedDate}
711
+ timeSlotGroups={timeSlotGroups}
712
+ setTimeSlotGroups={setTimeSlotGroups}
713
+ selectedTickets={selectedTickets}
714
+ handleTicketSelect={handleTicketSelect}
715
+ sortBySoldOut={sortBySoldOut}
716
+ ticketsHeaderComponent={ticketsHeaderComponent}
717
+ hideTicketsHeader={hideTicketsHeader}
718
+ showGroupNameBlock={showGroupNameBlock}
719
+ currencySymbol={currencySymbol}
720
+ isSeatMapAllowed={isSeatMapAllowed}
721
+ getTimeSlots={getTimeSlots}
722
+ />
723
+ )}
724
+ {!isSalesClosed && !isTimeSlotEvent && (
617
725
  <TicketsSection
618
726
  event={event}
619
- ticketsList={ordinarTickets}
727
+ ticketsList={ordinaryTickets}
620
728
  tableTickets={tableTickets}
621
729
  selectedTickets={selectedTickets}
622
730
  handleTicketSelect={handleTicketSelect}
@@ -624,15 +732,17 @@ export const TicketsContainer = ({
624
732
  ticketsHeaderComponent={ticketsHeaderComponent}
625
733
  tableTicketsHeaderComponent={tableTicketsHeaderComponent}
626
734
  hideTableTicketsHeader={hideTableTicketsHeader || _isEmpty(tableTickets)}
627
- hideTicketsHeader={hideTicketsHeader || _isEmpty(ordinarTickets)}
735
+ hideTicketsHeader={hideTicketsHeader || _isEmpty(ordinaryTickets)}
628
736
  showGroupNameBlock={showGroupNameBlock}
629
- currencySybmol={currencySybmol}
737
+ currencySymbol={currencySymbol}
630
738
  isSeatMapAllowed={isSeatMapAllowed}
631
739
  />
632
740
  )}
633
741
  {externalUrl ? null : isSalesClosed ? (
634
742
  <p
635
- className={`event-closed-message ${!isLoggedIn ? 'event-closed-on-bottom' : ''}`}
743
+ className={`event-closed-message ${
744
+ !isLoggedIn ? 'event-closed-on-bottom' : ''
745
+ }`}
636
746
  >
637
747
  Sales for this event are closed.
638
748
  </p>
@@ -650,7 +760,7 @@ export const TicketsContainer = ({
650
760
  ) : null}
651
761
  {showWaitingList && event.salesStarted && !hideWaitingList && (
652
762
  <WaitingList
653
- tickets={ordinarTickets}
763
+ tickets={ordinaryTickets}
654
764
  eventId={eventId}
655
765
  defaultMaxQuantity={event.waitingListMaxQuantity}
656
766
  />
@@ -682,16 +792,16 @@ export const TicketsContainer = ({
682
792
  {canShowGetTicketBtn() && (
683
793
  <Button
684
794
  aria-hidden={true}
685
- className={`book-button
686
- ${bookButtonIsDisabled ? 'disabled' : ''}
795
+ className={`book-button
796
+ ${bookButtonIsDisabled ? 'disabled' : ''}
687
797
  ${isButtonScrollable ? 'is-scrollable' : ''}
688
798
  ${!isLoggedIn ? 'on-bottom' : ''}
689
799
  `}
690
800
  onClick={
691
801
  event?.salesStart &&
692
- event?.presalesStarted &&
693
- !event?.presalesEnded &&
694
- (!isPreregistred || !isLogged)
802
+ event?.presalesStarted &&
803
+ !event?.presalesEnded &&
804
+ (!isPreregistered || !isLogged)
695
805
  ? handleNotAvailableTicketsClick
696
806
  : handleGetTicketClick
697
807
  }
@@ -3,6 +3,7 @@ import './style.css'
3
3
  import _identity from 'lodash/identity'
4
4
  import React, { memo, useState } from 'react'
5
5
  import Countdown from 'react-countdown'
6
+ import ReactDOM from 'react-dom'
6
7
  import SVG from 'react-inlinesvg'
7
8
 
8
9
  import Cross from '../../assets/images/cross.svg'
@@ -12,6 +13,7 @@ export interface ITimerWidgetPage {
12
13
  expires_at: number;
13
14
  buyLoading?: boolean;
14
15
  onCountdownFinish?: () => void;
16
+ container?: string;
15
17
  }
16
18
 
17
19
  export interface IRenderer {
@@ -25,6 +27,7 @@ const TimerWidget = ({
25
27
  expires_at,
26
28
  buyLoading,
27
29
  onCountdownFinish = _identity,
30
+ container,
28
31
  }: ITimerWidgetPage) => {
29
32
  const [showTimer, setShowTimer] = useState(true)
30
33
 
@@ -59,9 +62,9 @@ const TimerWidget = ({
59
62
  }
60
63
  }
61
64
 
62
- return showTimer && !!expires_at ? (
65
+ const timerComponent = (
63
66
  <div className="timer">
64
- <div className="close-icon" onClick={hideTimer}>
67
+ <div aria-hidden className="close-icon" onClick={hideTimer}>
65
68
  <SVG src={Cross} width="10" height="10" fill="#fff" />
66
69
  </div>
67
70
  <div className="toast-message">
@@ -79,7 +82,16 @@ const TimerWidget = ({
79
82
  </p>
80
83
  </div>
81
84
  </div>
82
- ) : null
85
+ )
86
+
87
+ return showTimer && !!expires_at
88
+ ? container
89
+ ? ReactDOM.createPortal(
90
+ timerComponent,
91
+ document.querySelector(container) as HTMLElement
92
+ )
93
+ : timerComponent
94
+ : null
83
95
  }
84
96
 
85
97
  export default memo(TimerWidget)
@@ -1 +1,3 @@
1
1
  export const X_TF_ECOMMERCE = 'X-TF-ECOMMERCE'
2
+ export const FEES_STYLES = { TRADITIONAL: 'TRADITIONAL', DISPLAY_BOTH: 'DISPLAY_BOTH' }
3
+ export const DEFAULT_FEES_STYLE = FEES_STYLES.TRADITIONAL