tf-checkout-react 1.7.31 → 2.0.0-beta.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 (454) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1033
  3. package/dist/checkout.css +17 -0
  4. package/dist/index.d.mts +3946 -0
  5. package/dist/index.d.ts +3946 -30
  6. package/dist/index.esm.js +9176 -0
  7. package/dist/index.esm.js.map +1 -0
  8. package/dist/index.js +9330 -6
  9. package/dist/index.js.map +1 -0
  10. package/dist/styles/addons.css +75 -0
  11. package/dist/styles/auth.css +294 -0
  12. package/dist/styles/billing.css +98 -0
  13. package/dist/styles/confirmation.css +51 -0
  14. package/dist/styles/foundation.css +146 -0
  15. package/dist/styles/payment.css +179 -0
  16. package/dist/styles/primitives.css +356 -0
  17. package/dist/styles/seat-map.css +46 -0
  18. package/dist/styles/tickets.css +285 -0
  19. package/dist/styles/verification-confirmation.css +258 -0
  20. package/package.json +93 -78
  21. package/src/.d.ts +1 -1
  22. package/src/CardInput.tsx +374 -0
  23. package/src/EmbeddedCheckout.tsx +400 -0
  24. package/src/ResumedCheckout.tsx +132 -0
  25. package/src/auth/AuthModal.tsx +238 -0
  26. package/src/auth/ForgotPasswordForm.tsx +93 -0
  27. package/src/auth/LoginForm.tsx +104 -0
  28. package/src/auth/PasswordInput.tsx +33 -0
  29. package/src/auth/RegisterForm.tsx +333 -0
  30. package/src/auth/ResetPasswordForm.tsx +112 -0
  31. package/src/auth/index.ts +5 -0
  32. package/src/checkout.css +17 -0
  33. package/src/configure.ts +29 -0
  34. package/src/confirmer.ts +439 -0
  35. package/src/index.ts +41 -30
  36. package/src/instance.ts +21 -0
  37. package/src/sections/AddOns.tsx +241 -0
  38. package/src/sections/BillingInfo.tsx +790 -0
  39. package/src/sections/Confirmation.tsx +146 -0
  40. package/src/sections/IDVerification.tsx +76 -0
  41. package/src/sections/OrderConfirmation.tsx +277 -0
  42. package/src/sections/OrderIDVerification.tsx +117 -0
  43. package/src/sections/OrderSummary.tsx +84 -0
  44. package/src/sections/Payment.tsx +557 -0
  45. package/src/sections/SeatMap.tsx +512 -0
  46. package/src/sections/Tickets.tsx +573 -0
  47. package/src/sections/billing/BillingAddressFields.tsx +138 -0
  48. package/src/sections/billing/BuyerField.tsx +75 -0
  49. package/src/sections/billing/TicketHoldersCard.tsx +124 -0
  50. package/src/sections/billing/fieldRules.ts +43 -0
  51. package/src/sections/billing/types.ts +10 -0
  52. package/src/sections/index.ts +15 -0
  53. package/src/sections/seatMap.utils.ts +105 -0
  54. package/src/styles/addons.css +75 -0
  55. package/src/styles/auth.css +294 -0
  56. package/src/styles/billing.css +98 -0
  57. package/src/styles/confirmation.css +51 -0
  58. package/src/styles/foundation.css +146 -0
  59. package/src/styles/payment.css +179 -0
  60. package/src/styles/primitives.css +356 -0
  61. package/src/styles/seat-map.css +46 -0
  62. package/src/styles/tickets.css +285 -0
  63. package/src/styles/verification-confirmation.css +258 -0
  64. package/src/typings.d.ts +2 -2
  65. package/src/ui/Button.tsx +38 -0
  66. package/src/ui/Card.tsx +54 -0
  67. package/src/ui/CheckoutProvider.tsx +172 -0
  68. package/src/ui/CustomFields.tsx +195 -0
  69. package/src/ui/Field.tsx +114 -0
  70. package/src/ui/I18nProvider.tsx +36 -0
  71. package/src/ui/InstallmentPicker.tsx +69 -0
  72. package/src/ui/Loader.tsx +12 -0
  73. package/src/ui/PaymentMethods.tsx +133 -0
  74. package/src/ui/PriceLine.tsx +24 -0
  75. package/src/ui/QuantitySelect.tsx +50 -0
  76. package/src/ui/Stepper.tsx +36 -0
  77. package/src/ui/Timer.tsx +34 -0
  78. package/src/ui/TimerBanner.tsx +55 -0
  79. package/src/ui/cx.ts +3 -0
  80. package/src/ui/index.ts +54 -0
  81. package/src/ui/theme.ts +83 -0
  82. package/src/useCheckout.ts +35 -0
  83. package/src/xendit.ts +438 -0
  84. package/dist/adapters/customFields.d.ts +0 -12
  85. package/dist/adapters/index.d.ts +0 -1
  86. package/dist/api/auth.d.ts +0 -27
  87. package/dist/api/cart.d.ts +0 -2
  88. package/dist/api/checkout.d.ts +0 -3
  89. package/dist/api/common.d.ts +0 -11
  90. package/dist/api/guestTicketDelegation.d.ts +0 -47
  91. package/dist/api/index.d.ts +0 -82
  92. package/dist/api/interceptors.d.ts +0 -1
  93. package/dist/api/orders.d.ts +0 -2
  94. package/dist/api/payment.d.ts +0 -5
  95. package/dist/api/preRegistrationComplete.d.ts +0 -3
  96. package/dist/api/publicRequest.d.ts +0 -9
  97. package/dist/api/resale.d.ts +0 -5
  98. package/dist/components/account-settings/index.d.ts +0 -3
  99. package/dist/components/addonsContainer/AddonComponent.d.ts +0 -18
  100. package/dist/components/addonsContainer/SimpleAddonsContainer.d.ts +0 -20
  101. package/dist/components/addonsContainer/adapters/index.d.ts +0 -12
  102. package/dist/components/addonsContainer/index.d.ts +0 -22
  103. package/dist/components/addonsContainer/normalizers/index.d.ts +0 -1
  104. package/dist/components/addonsContainer/useAddons.d.ts +0 -23
  105. package/dist/components/addonsContainer/utils/index.d.ts +0 -16
  106. package/dist/components/billing-info-container/hooks/index.d.ts +0 -3
  107. package/dist/components/billing-info-container/hooks/usePaymentContext.d.ts +0 -5
  108. package/dist/components/billing-info-container/hooks/usePaymentRedirect.d.ts +0 -14
  109. package/dist/components/billing-info-container/hooks/useStripePayment.d.ts +0 -18
  110. package/dist/components/billing-info-container/index.d.ts +0 -69
  111. package/dist/components/billing-info-container/utils.d.ts +0 -59
  112. package/dist/components/common/Checkbox.d.ts +0 -11
  113. package/dist/components/common/CheckboxField/index.d.ts +0 -9
  114. package/dist/components/common/CopyField.d.ts +0 -12
  115. package/dist/components/common/CopyMessageModal.d.ts +0 -7
  116. package/dist/components/common/CustomField.d.ts +0 -26
  117. package/dist/components/common/DatePickerField.d.ts +0 -20
  118. package/dist/components/common/FieldSection/index.d.ts +0 -19
  119. package/dist/components/common/FieldSection/utils/index.d.ts +0 -8
  120. package/dist/components/common/FormikPhoneNumberField.d.ts +0 -11
  121. package/dist/components/common/Loader.d.ts +0 -2
  122. package/dist/components/common/ModalComponent/index.d.ts +0 -16
  123. package/dist/components/common/NativeSelectFeild/index.d.ts +0 -21
  124. package/dist/components/common/PhoneNumberField.d.ts +0 -14
  125. package/dist/components/common/PoweredBy.d.ts +0 -2
  126. package/dist/components/common/RadioField.d.ts +0 -11
  127. package/dist/components/common/RadioGroupField/index.d.ts +0 -17
  128. package/dist/components/common/RedirectModal.d.ts +0 -7
  129. package/dist/components/common/SelectField/index.d.ts +0 -19
  130. package/dist/components/common/SnackbarAlert.d.ts +0 -13
  131. package/dist/components/common/index.d.ts +0 -11
  132. package/dist/components/common/socials.d.ts +0 -10
  133. package/dist/components/confirmModal/index.d.ts +0 -10
  134. package/dist/components/confirmationContainer/config.d.ts +0 -1
  135. package/dist/components/confirmationContainer/index.d.ts +0 -46
  136. package/dist/components/confirmationContainer/social-buttons.d.ts +0 -16
  137. package/dist/components/countdown/index.d.ts +0 -14
  138. package/dist/components/delegationsContainer/IssueComponent.d.ts +0 -10
  139. package/dist/components/delegationsContainer/IssueTicketForm.d.ts +0 -9
  140. package/dist/components/delegationsContainer/TicketsAssignedTable.d.ts +0 -11
  141. package/dist/components/delegationsContainer/TicketsAvailableTable.d.ts +0 -11
  142. package/dist/components/delegationsContainer/index.d.ts +0 -10
  143. package/dist/components/forgotPasswordModal/index.d.ts +0 -12
  144. package/dist/components/idVerificationContainer/VerificationPendingModal.d.ts +0 -10
  145. package/dist/components/idVerificationContainer/constants.d.ts +0 -21
  146. package/dist/components/idVerificationContainer/index.d.ts +0 -16
  147. package/dist/components/index.d.ts +0 -12
  148. package/dist/components/loginForm/index.d.ts +0 -31
  149. package/dist/components/loginModal/SignUpForm.d.ts +0 -10
  150. package/dist/components/loginModal/constants.d.ts +0 -39
  151. package/dist/components/loginModal/index.d.ts +0 -22
  152. package/dist/components/myTicketsContainer/index.d.ts +0 -16
  153. package/dist/components/myTicketsContainer/row.d.ts +0 -3
  154. package/dist/components/myTicketsContainer/tableConfig.d.ts +0 -5
  155. package/dist/components/orderDetailsContainer/CustomFieldsForm.d.ts +0 -12
  156. package/dist/components/orderDetailsContainer/TicketHolderCustomFields.d.ts +0 -10
  157. package/dist/components/orderDetailsContainer/index.d.ts +0 -66
  158. package/dist/components/orderDetailsContainer/ticketsTable.d.ts +0 -46
  159. package/dist/components/orderDetailsContainer/utils/index.d.ts +0 -6
  160. package/dist/components/paymentContainer/OrderDetails.d.ts +0 -9
  161. package/dist/components/paymentContainer/PaymentPlanSection.d.ts +0 -10
  162. package/dist/components/paymentContainer/handlePayment.d.ts +0 -15
  163. package/dist/components/paymentContainer/index.d.ts +0 -39
  164. package/dist/components/preRegistration/FieldsSection.d.ts +0 -19
  165. package/dist/components/preRegistration/Influancers.d.ts +0 -2
  166. package/dist/components/preRegistration/PreRegistrationComplete.d.ts +0 -13
  167. package/dist/components/preRegistration/PreRegistrationInformations.d.ts +0 -2
  168. package/dist/components/preRegistration/Prewards.d.ts +0 -2
  169. package/dist/components/preRegistration/ShareOptions.d.ts +0 -2
  170. package/dist/components/preRegistration/constants.d.ts +0 -2
  171. package/dist/components/preRegistration/index.d.ts +0 -23
  172. package/dist/components/preRegistration/utils.d.ts +0 -12
  173. package/dist/components/registerForm/adapters/index.d.ts +0 -4
  174. package/dist/components/registerForm/constants.d.ts +0 -1
  175. package/dist/components/registerForm/index.d.ts +0 -16
  176. package/dist/components/registerModal/index.d.ts +0 -12
  177. package/dist/components/resetPasswordContainer/index.d.ts +0 -10
  178. package/dist/components/rsvpContainer/index.d.ts +0 -7
  179. package/dist/components/seatMapContainer/SeatMapComponent.d.ts +0 -8
  180. package/dist/components/seatMapContainer/TicketsSection.d.ts +0 -9
  181. package/dist/components/seatMapContainer/addToCart.d.ts +0 -14
  182. package/dist/components/seatMapContainer/index.d.ts +0 -2
  183. package/dist/components/seatMapContainer/utils.d.ts +0 -6
  184. package/dist/components/signupModal/index.d.ts +0 -12
  185. package/dist/components/ticketResale/index.d.ts +0 -12
  186. package/dist/components/ticketResaleModal/index.d.ts +0 -19
  187. package/dist/components/ticketsContainer/AccessCodeSection.d.ts +0 -7
  188. package/dist/components/ticketsContainer/InfoIcon.d.ts +0 -5
  189. package/dist/components/ticketsContainer/PromoCodeSection.d.ts +0 -15
  190. package/dist/components/ticketsContainer/ReferralLogic.d.ts +0 -5
  191. package/dist/components/ticketsContainer/TicketRow.d.ts +0 -13
  192. package/dist/components/ticketsContainer/TicketsSection.d.ts +0 -20
  193. package/dist/components/ticketsContainer/TimeSlotTicketRow.d.ts +0 -19
  194. package/dist/components/ticketsContainer/TimeSlotsSection.d.ts +0 -25
  195. package/dist/components/ticketsContainer/index.d.ts +0 -93
  196. package/dist/components/ticketsContainer/utils.d.ts +0 -13
  197. package/dist/components/timerWidget/index.d.ts +0 -16
  198. package/dist/components/waitingList/index.d.ts +0 -9
  199. package/dist/constants/index.d.ts +0 -7
  200. package/dist/env.d.ts +0 -16
  201. package/dist/hoc/CustomFields/index.d.ts +0 -3
  202. package/dist/hoc/index.d.ts +0 -1
  203. package/dist/hooks/index.d.ts +0 -1
  204. package/dist/hooks/useCookieListener.d.ts +0 -1
  205. package/dist/hooks/useEventListener.d.ts +0 -1
  206. package/dist/hooks/useLocalStorageListener.d.ts +0 -1
  207. package/dist/hooks/useOnline.d.ts +0 -1
  208. package/dist/hooks/usePixel.d.ts +0 -6
  209. package/dist/images/Polygon.png +0 -0
  210. package/dist/images/cross.svg +0 -44
  211. package/dist/images/done.svg +0 -3
  212. package/dist/images/edit.svg +0 -10
  213. package/dist/images/email.svg +0 -1
  214. package/dist/images/instagram.svg +0 -5
  215. package/dist/images/logo-ttf.png +0 -0
  216. package/dist/images/spotify.svg +0 -6
  217. package/dist/images/user.svg +0 -1
  218. package/dist/images/xmark.svg +0 -92
  219. package/dist/normalizers/index.d.ts +0 -4
  220. package/dist/tf-checkout-react.cjs.development.js +0 -16149
  221. package/dist/tf-checkout-react.cjs.development.js.map +0 -1
  222. package/dist/tf-checkout-react.cjs.production.min.js +0 -2
  223. package/dist/tf-checkout-react.cjs.production.min.js.map +0 -1
  224. package/dist/tf-checkout-react.esm.js +0 -16116
  225. package/dist/tf-checkout-react.esm.js.map +0 -1
  226. package/dist/tf-checkout-styles.css +0 -1
  227. package/dist/types/add_on.d.ts +0 -8
  228. package/dist/types/billing-info-data.d.ts +0 -27
  229. package/dist/types/checkoutPageConfigs.d.ts +0 -13
  230. package/dist/types/index.d.ts +0 -6
  231. package/dist/types/order-data.d.ts +0 -15
  232. package/dist/types/payment-field.d.ts +0 -6
  233. package/dist/types/payment-plan-configuration.d.ts +0 -18
  234. package/dist/types/referral-promotion.d.ts +0 -6
  235. package/dist/types/verification.d.ts +0 -12
  236. package/dist/utils/adaptStripeErrors.d.ts +0 -8
  237. package/dist/utils/auth.d.ts +0 -21
  238. package/dist/utils/cookies.d.ts +0 -3
  239. package/dist/utils/createCheckoutDataBodyWithDefaultHolder.d.ts +0 -23
  240. package/dist/utils/createMarkup.d.ts +0 -3
  241. package/dist/utils/customFields.d.ts +0 -35
  242. package/dist/utils/downloadPDF.d.ts +0 -1
  243. package/dist/utils/form.d.ts +0 -3
  244. package/dist/utils/formikErrorFocus.d.ts +0 -2
  245. package/dist/utils/getDomain.d.ts +0 -1
  246. package/dist/utils/getImage.d.ts +0 -1
  247. package/dist/utils/getQueryVariable.d.ts +0 -1
  248. package/dist/utils/htmlNodeFromString.d.ts +0 -1
  249. package/dist/utils/index.d.ts +0 -15
  250. package/dist/utils/isBrowser.d.ts +0 -1
  251. package/dist/utils/isValidNumber.d.ts +0 -1
  252. package/dist/utils/jsonUtils.d.ts +0 -1
  253. package/dist/utils/replaceVarInString.d.ts +0 -1
  254. package/dist/utils/setConfigs.d.ts +0 -11
  255. package/dist/utils/showZero.d.ts +0 -1
  256. package/dist/validators/index.d.ts +0 -8
  257. package/src/adapters/customFields.ts +0 -85
  258. package/src/adapters/index.ts +0 -1
  259. package/src/api/auth.ts +0 -88
  260. package/src/api/cart.ts +0 -28
  261. package/src/api/checkout.ts +0 -28
  262. package/src/api/common.ts +0 -190
  263. package/src/api/guestTicketDelegation.ts +0 -79
  264. package/src/api/index.ts +0 -346
  265. package/src/api/interceptors.ts +0 -77
  266. package/src/api/orders.ts +0 -30
  267. package/src/api/payment.ts +0 -77
  268. package/src/api/preRegistrationComplete.ts +0 -37
  269. package/src/api/publicRequest.ts +0 -47
  270. package/src/api/resale.ts +0 -28
  271. package/src/assets/images/Polygon.png +0 -0
  272. package/src/assets/images/cross.svg +0 -44
  273. package/src/assets/images/done.svg +0 -3
  274. package/src/assets/images/edit.svg +0 -10
  275. package/src/assets/images/email.svg +0 -1
  276. package/src/assets/images/instagram.svg +0 -5
  277. package/src/assets/images/logo-ttf.png +0 -0
  278. package/src/assets/images/spotify.svg +0 -6
  279. package/src/assets/images/user.svg +0 -1
  280. package/src/assets/images/xmark.svg +0 -92
  281. package/src/components/account-settings/index.tsx +0 -161
  282. package/src/components/account-settings/style.css +0 -200
  283. package/src/components/addonsContainer/AddonComponent.tsx +0 -204
  284. package/src/components/addonsContainer/SimpleAddonsContainer.tsx +0 -459
  285. package/src/components/addonsContainer/adapters/index.tsx +0 -62
  286. package/src/components/addonsContainer/index.tsx +0 -353
  287. package/src/components/addonsContainer/normalizers/index.ts +0 -5
  288. package/src/components/addonsContainer/useAddons.ts +0 -248
  289. package/src/components/addonsContainer/utils/index.tsx +0 -231
  290. package/src/components/billing-info-container/hooks/index.ts +0 -3
  291. package/src/components/billing-info-container/hooks/usePaymentContext.ts +0 -22
  292. package/src/components/billing-info-container/hooks/usePaymentRedirect.ts +0 -145
  293. package/src/components/billing-info-container/hooks/useStripePayment.ts +0 -119
  294. package/src/components/billing-info-container/index.tsx +0 -1695
  295. package/src/components/billing-info-container/style.css +0 -150
  296. package/src/components/billing-info-container/utils.tsx +0 -413
  297. package/src/components/common/Checkbox.tsx +0 -51
  298. package/src/components/common/CheckboxField/index.tsx +0 -41
  299. package/src/components/common/CopyField.tsx +0 -53
  300. package/src/components/common/CopyMessageModal.tsx +0 -34
  301. package/src/components/common/CustomField.tsx +0 -168
  302. package/src/components/common/DatePickerField.tsx +0 -126
  303. package/src/components/common/FieldSection/index.tsx +0 -141
  304. package/src/components/common/FieldSection/utils/index.tsx +0 -92
  305. package/src/components/common/FormikPhoneNumberField.tsx +0 -51
  306. package/src/components/common/Loader.tsx +0 -8
  307. package/src/components/common/ModalComponent/index.tsx +0 -68
  308. package/src/components/common/NativeSelectFeild/index.tsx +0 -86
  309. package/src/components/common/PhoneNumberField.tsx +0 -117
  310. package/src/components/common/PoweredBy.tsx +0 -15
  311. package/src/components/common/RadioField.tsx +0 -36
  312. package/src/components/common/RadioGroupField/index.tsx +0 -76
  313. package/src/components/common/RedirectModal.tsx +0 -43
  314. package/src/components/common/SelectField/index.tsx +0 -104
  315. package/src/components/common/SnackbarAlert.tsx +0 -52
  316. package/src/components/common/index.tsx +0 -11
  317. package/src/components/common/socials.tsx +0 -33
  318. package/src/components/confirmModal/index.tsx +0 -60
  319. package/src/components/confirmationContainer/config.ts +0 -79
  320. package/src/components/confirmationContainer/index.tsx +0 -349
  321. package/src/components/confirmationContainer/social-buttons.tsx +0 -149
  322. package/src/components/confirmationContainer/style.css +0 -206
  323. package/src/components/countdown/index.tsx +0 -100
  324. package/src/components/countdown/style.css +0 -10
  325. package/src/components/delegationsContainer/IssueComponent.tsx +0 -156
  326. package/src/components/delegationsContainer/IssueTicketForm.tsx +0 -109
  327. package/src/components/delegationsContainer/TicketsAssignedTable.tsx +0 -55
  328. package/src/components/delegationsContainer/TicketsAvailableTable.tsx +0 -54
  329. package/src/components/delegationsContainer/index.tsx +0 -83
  330. package/src/components/forgotPasswordModal/index.tsx +0 -138
  331. package/src/components/forgotPasswordModal/style.css +0 -47
  332. package/src/components/idVerificationContainer/VerificationPendingModal.tsx +0 -47
  333. package/src/components/idVerificationContainer/constants.ts +0 -27
  334. package/src/components/idVerificationContainer/index.tsx +0 -323
  335. package/src/components/index.ts +0 -12
  336. package/src/components/loginForm/index.tsx +0 -202
  337. package/src/components/loginModal/SignUpForm.tsx +0 -329
  338. package/src/components/loginModal/constants.ts +0 -46
  339. package/src/components/loginModal/index.tsx +0 -263
  340. package/src/components/loginModal/style.css +0 -116
  341. package/src/components/myTicketsContainer/index.tsx +0 -228
  342. package/src/components/myTicketsContainer/row.tsx +0 -46
  343. package/src/components/myTicketsContainer/style.css +0 -40
  344. package/src/components/myTicketsContainer/tableConfig.tsx +0 -80
  345. package/src/components/myTicketsContainer/types.d.ts +0 -41
  346. package/src/components/orderDetailsContainer/CustomFieldsForm.tsx +0 -75
  347. package/src/components/orderDetailsContainer/TicketHolderCustomFields.tsx +0 -100
  348. package/src/components/orderDetailsContainer/index.tsx +0 -586
  349. package/src/components/orderDetailsContainer/style.css +0 -77
  350. package/src/components/orderDetailsContainer/ticketsTable.tsx +0 -246
  351. package/src/components/orderDetailsContainer/utils/index.tsx +0 -55
  352. package/src/components/paymentContainer/OrderDetails.tsx +0 -277
  353. package/src/components/paymentContainer/PaymentPlanSection.tsx +0 -169
  354. package/src/components/paymentContainer/handlePayment.ts +0 -86
  355. package/src/components/paymentContainer/index.tsx +0 -643
  356. package/src/components/paymentContainer/style.css +0 -196
  357. package/src/components/preRegistration/FieldsSection.tsx +0 -150
  358. package/src/components/preRegistration/Influancers.tsx +0 -34
  359. package/src/components/preRegistration/PreRegistrationComplete.tsx +0 -186
  360. package/src/components/preRegistration/PreRegistrationInformations.tsx +0 -60
  361. package/src/components/preRegistration/Prewards.tsx +0 -41
  362. package/src/components/preRegistration/ShareOptions.tsx +0 -77
  363. package/src/components/preRegistration/constants.tsx +0 -169
  364. package/src/components/preRegistration/index.tsx +0 -330
  365. package/src/components/preRegistration/style.css +0 -3
  366. package/src/components/preRegistration/utils.ts +0 -163
  367. package/src/components/registerForm/adapters/index.tsx +0 -10
  368. package/src/components/registerForm/constants.tsx +0 -98
  369. package/src/components/registerForm/index.tsx +0 -188
  370. package/src/components/registerModal/index.tsx +0 -154
  371. package/src/components/registerModal/style.css +0 -18
  372. package/src/components/resetPasswordContainer/index.tsx +0 -102
  373. package/src/components/resetPasswordContainer/style.css +0 -36
  374. package/src/components/rsvpContainer/index.tsx +0 -126
  375. package/src/components/seatMapContainer/SeatMapComponent.tsx +0 -77
  376. package/src/components/seatMapContainer/TicketsSection.tsx +0 -286
  377. package/src/components/seatMapContainer/addToCart.ts +0 -66
  378. package/src/components/seatMapContainer/index.tsx +0 -443
  379. package/src/components/seatMapContainer/utils.ts +0 -133
  380. package/src/components/signupModal/index.tsx +0 -178
  381. package/src/components/signupModal/style.css +0 -58
  382. package/src/components/ticketResale/index.tsx +0 -86
  383. package/src/components/ticketResaleModal/index.tsx +0 -224
  384. package/src/components/ticketResaleModal/style.css +0 -28
  385. package/src/components/ticketsContainer/AccessCodeSection.tsx +0 -50
  386. package/src/components/ticketsContainer/InfoIcon.tsx +0 -35
  387. package/src/components/ticketsContainer/PromoCodeSection.tsx +0 -116
  388. package/src/components/ticketsContainer/ReferralLogic.tsx +0 -33
  389. package/src/components/ticketsContainer/TicketRow.tsx +0 -107
  390. package/src/components/ticketsContainer/TicketsSection.tsx +0 -350
  391. package/src/components/ticketsContainer/TimeSlotTicketRow.tsx +0 -243
  392. package/src/components/ticketsContainer/TimeSlotsSection.tsx +0 -194
  393. package/src/components/ticketsContainer/index.tsx +0 -986
  394. package/src/components/ticketsContainer/style.css +0 -178
  395. package/src/components/ticketsContainer/utils.ts +0 -35
  396. package/src/components/timerWidget/index.tsx +0 -97
  397. package/src/components/timerWidget/style.css +0 -35
  398. package/src/components/waitingList/index.tsx +0 -190
  399. package/src/components/waitingList/style.css +0 -27
  400. package/src/constants/index.ts +0 -3
  401. package/src/env.ts +0 -29
  402. package/src/hoc/CustomFields/index.tsx +0 -85
  403. package/src/hoc/index.ts +0 -1
  404. package/src/hooks/index.ts +0 -1
  405. package/src/hooks/useCookieListener.ts +0 -32
  406. package/src/hooks/useEventListener.ts +0 -32
  407. package/src/hooks/useLocalStorageListener.ts +0 -32
  408. package/src/hooks/useOnline.ts +0 -21
  409. package/src/hooks/usePixel.ts +0 -88
  410. package/src/normalizers/index.ts +0 -49
  411. package/src/types/add_on.ts +0 -8
  412. package/src/types/api/auth.d.ts +0 -63
  413. package/src/types/api/axiosResponse.d.ts +0 -6
  414. package/src/types/api/cart.d.ts +0 -73
  415. package/src/types/api/checkout.d.ts +0 -92
  416. package/src/types/api/common.d.ts +0 -153
  417. package/src/types/api/guestTicketDelegation.d.ts +0 -18
  418. package/src/types/api/orders.d.ts +0 -116
  419. package/src/types/api/payment.d.ts +0 -180
  420. package/src/types/api/preRegistration.d.ts +0 -11
  421. package/src/types/api/preRegistrationComplete.d.ts +0 -95
  422. package/src/types/api/ticketResale.d.ts +0 -13
  423. package/src/types/billing-info-data.ts +0 -37
  424. package/src/types/checkoutPageConfigs.ts +0 -13
  425. package/src/types/formFields.d.ts +0 -29
  426. package/src/types/index.ts +0 -6
  427. package/src/types/order-data.ts +0 -16
  428. package/src/types/payment-field.ts +0 -7
  429. package/src/types/payment-plan-configuration.ts +0 -19
  430. package/src/types/pre-registration-complete.d.ts +0 -47
  431. package/src/types/referral-promotion.ts +0 -7
  432. package/src/types/seatMap.d.ts +0 -159
  433. package/src/types/verification.ts +0 -12
  434. package/src/utils/adaptStripeErrors.ts +0 -26
  435. package/src/utils/auth.ts +0 -45
  436. package/src/utils/cookies.ts +0 -73
  437. package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +0 -81
  438. package/src/utils/createMarkup.ts +0 -1
  439. package/src/utils/customFields.ts +0 -80
  440. package/src/utils/downloadPDF.tsx +0 -42
  441. package/src/utils/form.ts +0 -34
  442. package/src/utils/formikErrorFocus.ts +0 -24
  443. package/src/utils/getDomain.ts +0 -21
  444. package/src/utils/getImage.ts +0 -14
  445. package/src/utils/getQueryVariable.ts +0 -13
  446. package/src/utils/htmlNodeFromString.ts +0 -6
  447. package/src/utils/index.ts +0 -15
  448. package/src/utils/isBrowser.ts +0 -2
  449. package/src/utils/isValidNumber.ts +0 -8
  450. package/src/utils/jsonUtils.ts +0 -8
  451. package/src/utils/replaceVarInString.ts +0 -9
  452. package/src/utils/setConfigs.ts +0 -24
  453. package/src/utils/showZero.tsx +0 -10
  454. package/src/validators/index.ts +0 -61
@@ -0,0 +1,790 @@
1
+ /**
2
+ * `<BillingInfo/>` — the buyer + attendee details step. Collects: the buyer's
3
+ * name/email + event-gated buyer fields (Instagram/nationality/gender), any
4
+ * ORDER-level custom fields, PER-HOLDER name + ticket-level custom fields (when
5
+ * the event requires holder names or has ticket-scoped fields), and marketing
6
+ * opt-ins. Writes it all into the engine (`setBilling` with `dataCapture`,
7
+ * `setHolders` with `ticketDataCapture`, `setConsent`) so `<Payment/>`'s
8
+ * pre-flight passes. Use it alongside `<Payment/>`, or set those yourself.
9
+ */
10
+ import React, {
11
+ Fragment,
12
+ useEffect,
13
+ useMemo,
14
+ useRef,
15
+ useState,
16
+ type ChangeEvent,
17
+ type FC,
18
+ type ReactNode,
19
+ } from 'react'
20
+
21
+ import {
22
+ hooks,
23
+ mapProfileToBilling,
24
+ missingCheckoutDetails,
25
+ isEmail,
26
+ ticketFieldsFor,
27
+ type CheckoutBilling,
28
+ type CheckoutHolder,
29
+ type CustomFieldValues,
30
+ type EventCustomFieldGroup,
31
+ } from 'tf-checkout-shared'
32
+ import { PhoneInput, type CountryIso2 } from 'react-international-phone'
33
+
34
+ import { Button } from '../ui/Button'
35
+ import { Card, SectionHeader } from '../ui/Card'
36
+ import { useCheckoutContext, useCheckoutValidation } from '../ui/CheckoutProvider'
37
+ import { CustomFieldInput } from '../ui/CustomFields'
38
+ import { Checkbox, Field, FieldRow, Input } from '../ui/Field'
39
+ import { BillingAddressFields } from './billing/BillingAddressFields'
40
+ import { BuyerField } from './billing/BuyerField'
41
+ import { DOB_MIN, isBlankPhone, toDateInputValue, todayISO } from './billing/fieldRules'
42
+ import { TicketHoldersCard } from './billing/TicketHoldersCard'
43
+ import type { BillingDraft, HolderDraft } from './billing/types'
44
+
45
+ export interface BillingInfoProps {
46
+ /** Event context is used to default the country and phone dial code. */
47
+ eventId?: string
48
+ /** Fired once billing is saved to the engine — e.g. reveal `<Payment/>`. */
49
+ onContinue?: () => void
50
+ /** Section heading. Old checkout uses "Billing Information". */
51
+ heading?: string
52
+ /** CTA label. */
53
+ buttonLabel?: string
54
+ /** When provided and the buyer isn't signed in, shows a "Login & skip ahead"
55
+ * row. The current email is passed when login follows an account match. */
56
+ onLogin?: (email?: string) => void
57
+ /** Hide this card only after a signed-in profile satisfies every ordinary
58
+ * billing requirement and the event needs no extra buyer/holder answers. */
59
+ skipWhenProfileComplete?: boolean
60
+ /** Ask guests to repeat their email and require an exact match. */
61
+ requireEmailConfirmation?: boolean
62
+ /** Always collect one named holder per selected ticket. EmbeddedCheckout uses
63
+ * this to match tf-sports; standalone sections default to backend gates. */
64
+ collectTicketHolders?: boolean
65
+ /** When holder details are enabled, also collect each holder's email and phone. */
66
+ collectHolderContact?: boolean
67
+ /** Marketing opt-in labels. Render a checkbox for each provided (unchecked). */
68
+ ttfOptInLabel?: string
69
+ brandOptInLabel?: string
70
+ /** Required host privacy acknowledgement. The URL is supplied by the consumer. */
71
+ privacyPolicy?: CheckoutPrivacyPolicy
72
+ /** Show the legacy inline "Required" error after a failed payment attempt. */
73
+ showPrivacyError?: boolean
74
+ /** Optional numbered badge (old checkout shows none — omitted by default). */
75
+ step?: ReactNode
76
+ className?: string
77
+ }
78
+
79
+ export interface CheckoutPrivacyPolicy {
80
+ url: string
81
+ brandName: string
82
+ linkLabel?: string
83
+ }
84
+
85
+ export const BillingInfo: FC<BillingInfoProps> = ({
86
+ eventId,
87
+ onContinue,
88
+ heading = 'Billing Information',
89
+ buttonLabel = 'Continue to payment',
90
+ onLogin,
91
+ skipWhenProfileComplete = false,
92
+ requireEmailConfirmation = false,
93
+ collectTicketHolders = false,
94
+ collectHolderContact = false,
95
+ ttfOptInLabel = 'Keep me updated about future events from The Ticket Fairy',
96
+ brandOptInLabel,
97
+ privacyPolicy,
98
+ showPrivacyError = false,
99
+ step,
100
+ className,
101
+ }) => {
102
+ const checkout = useCheckoutContext()
103
+ const validation = useCheckoutValidation()
104
+ const profile = hooks.useProfile({ retry: false })
105
+ const event = hooks.useEvent(eventId)
106
+ // No onContinue ⇒ this is one combined form (inside <EmbeddedCheckout/>): mirror the
107
+ // fields into the engine live and drop the "continue" button, so the buyer
108
+ // fills billing + pays with ONE button — the way the old checkout flowed.
109
+ const combined = !onContinue
110
+
111
+ const [billing, setBilling] = useState<BillingDraft>({
112
+ firstName: checkout.billing?.firstName ?? '',
113
+ lastName: checkout.billing?.lastName ?? '',
114
+ email: checkout.billing?.email ?? '',
115
+ confirmEmail: checkout.billing?.confirmEmail ?? '',
116
+ phone: checkout.billing?.phone,
117
+ streetAddress: checkout.billing?.streetAddress,
118
+ city: checkout.billing?.city,
119
+ zip: checkout.billing?.zip,
120
+ countryId: checkout.billing?.countryId,
121
+ stateId: checkout.billing?.stateId,
122
+ countryCode: checkout.billing?.countryCode,
123
+ stateCode: checkout.billing?.stateCode,
124
+ nationality: checkout.billing?.nationality,
125
+ gender: checkout.billing?.gender,
126
+ instagramHandle: checkout.billing?.instagramHandle,
127
+ })
128
+ const [dataCapture, setDataCapture] = useState<CustomFieldValues>(
129
+ checkout.billing?.dataCapture ?? {}
130
+ )
131
+ const [holders, setHolders] = useState<HolderDraft[]>(() =>
132
+ checkout.holders.map((holder) => ({
133
+ firstName: holder.firstName,
134
+ lastName: holder.lastName,
135
+ email: holder.email,
136
+ phone: holder.phone ?? '',
137
+ ticketTypeId: holder.ticketTypeId,
138
+ ticketDataCapture: holder.ticketDataCapture ?? {},
139
+ }))
140
+ )
141
+ const [consent, setConsent] = useState({ ttfOptIn: false, brandOptIn: false })
142
+ const [dob, setDob] = useState(() => {
143
+ const b = checkout.billing
144
+ if (!b?.dobDay || !b.dobMonth || !b.dobYear) return ''
145
+ return `${b.dobYear}-${String(b.dobMonth).padStart(2, '0')}-${String(
146
+ b.dobDay
147
+ ).padStart(2, '0')}`
148
+ }) // ISO yyyy-mm-dd
149
+ const restoredHolders = useRef(checkout.holders)
150
+ const profileSeeded = useRef(false)
151
+ const countryDefaultApplied = useRef(Boolean(checkout.billing?.countryId))
152
+
153
+ const gates = checkout.fieldGates
154
+
155
+ // Custom-field groups by scope.
156
+ const orderGroups = useMemo(
157
+ () => checkout.customFields.filter((g) => g.scope === 'order'),
158
+ [checkout.customFields]
159
+ )
160
+ const ticketGroups = useMemo(
161
+ () => checkout.customFields.filter((g) => g.scope === 'ticket'),
162
+ [checkout.customFields]
163
+ )
164
+ const ticketFields = useMemo(
165
+ () => ticketGroups.flatMap((g) => g.fields),
166
+ [ticketGroups]
167
+ )
168
+ const holderNamesRequired = checkout.holderNamesRequired || collectTicketHolders
169
+ const showHolders = holderNamesRequired || ticketFields.length > 0
170
+
171
+ // One holder slot per ticket, tagged with its ticket type (drives ticket-field
172
+ // scoping + the engine's per-holder validation).
173
+ const slots = useMemo(() => {
174
+ const out: { ticketTypeId?: string; ticketName?: string }[] = []
175
+ const lines = checkout.breakdown?.tickets ?? []
176
+ for (const line of lines)
177
+ for (let i = 0; i < line.quantity; i++)
178
+ out.push({ ticketTypeId: line.ticketTypeId, ticketName: line.name })
179
+ if (!out.length) {
180
+ const total = checkout.selections.reduce((n, s) => n + s.quantity, 0)
181
+ for (let i = 0; i < total; i++) out.push({})
182
+ }
183
+ return out
184
+ }, [checkout.breakdown, checkout.selections])
185
+
186
+ // The initial state above restores anything already on the engine. A profile
187
+ // that arrives later may fill blanks ONCE, but must never be consulted again
188
+ // while the buyer edits — otherwise clearing the final character restores the
189
+ // previous engine value on the next live commit.
190
+ useEffect(() => {
191
+ const p = profile.data
192
+ if (!p || profileSeeded.current) return
193
+ profileSeeded.current = true
194
+ const profileBilling = mapProfileToBilling(p)
195
+ setBilling((prev) => {
196
+ const next = {
197
+ ...prev,
198
+ firstName: prev.firstName || profileBilling.firstName || '',
199
+ lastName: prev.lastName || profileBilling.lastName || '',
200
+ email: prev.email || profileBilling.email || '',
201
+ confirmEmail:
202
+ prev.confirmEmail || (checkout.isLoggedIn ? profileBilling.email : '') || '',
203
+ phone:
204
+ (isBlankPhone(prev.phone) ? undefined : prev.phone) || profileBilling.phone,
205
+ streetAddress: prev.streetAddress || profileBilling.streetAddress,
206
+ city: prev.city || profileBilling.city,
207
+ zip: prev.zip || profileBilling.zip,
208
+ countryId: prev.countryId || profileBilling.countryId,
209
+ stateId: prev.stateId || profileBilling.stateId,
210
+ }
211
+ const keys = Object.keys(next) as (keyof BillingDraft)[]
212
+ return keys.every((k) => next[k] === prev[k]) ? prev : next
213
+ })
214
+ // Date of birth lives outside `billing`, so it has to be seeded here too —
215
+ // and it has to be here rather than in its own effect or in the useState
216
+ // initialiser: the initialiser runs before the profile resolves, and a
217
+ // separate effect would re-fire and overwrite the buyer, since the live
218
+ // commit below depends on `dob`. This effect already runs exactly once.
219
+ setDob((prev) => {
220
+ if (prev) return prev
221
+ const { dobYear, dobMonth, dobDay } = profileBilling
222
+ return toDateInputValue(dobYear, dobMonth, dobDay) ?? prev
223
+ })
224
+ }, [profile.data, checkout.isLoggedIn])
225
+
226
+ // Resize holders to the slot count, preserving typed values; tag ticket type.
227
+ useEffect(() => {
228
+ setHolders((prev) =>
229
+ slots.map((s, i) => ({
230
+ firstName: prev[i]?.firstName ?? restoredHolders.current[i]?.firstName ?? '',
231
+ lastName: prev[i]?.lastName ?? restoredHolders.current[i]?.lastName ?? '',
232
+ email: prev[i]?.email ?? restoredHolders.current[i]?.email ?? '',
233
+ phone: prev[i]?.phone ?? restoredHolders.current[i]?.phone ?? '',
234
+ ticketDataCapture:
235
+ prev[i]?.ticketDataCapture ??
236
+ restoredHolders.current[i]?.ticketDataCapture ??
237
+ {},
238
+ ticketTypeId: s.ticketTypeId,
239
+ ticketName: s.ticketName,
240
+ }))
241
+ )
242
+ }, [slots])
243
+
244
+ // Match the storefront: ticket 1 silently starts as the buyer. The legacy
245
+ // checkout has no extra "use buyer details" control.
246
+ useEffect(() => {
247
+ if (!showHolders) return
248
+ setHolders((prev) =>
249
+ prev.map((holder, index) =>
250
+ index === 0
251
+ ? {
252
+ ...holder,
253
+ firstName: billing.firstName,
254
+ lastName: billing.lastName,
255
+ email: billing.email,
256
+ phone: billing.phone ?? '',
257
+ }
258
+ : holder
259
+ )
260
+ )
261
+ }, [showHolders, billing.firstName, billing.lastName, billing.email, billing.phone])
262
+
263
+ // Debounced "this email already has an account" check → nudges the buyer to
264
+ // sign in (autofill + avoid duplicate accounts). Skips signed-in buyers.
265
+ useEffect(() => {
266
+ const email = billing.email.trim()
267
+ if (checkout.isLoggedIn || !isEmail(email)) return
268
+ const t = setTimeout(
269
+ () => checkout.checkBuyerEmail(email).catch(() => undefined),
270
+ 600
271
+ )
272
+ return () => clearTimeout(t)
273
+ // eslint-disable-next-line react-hooks/exhaustive-deps
274
+ }, [billing.email, checkout.isLoggedIn])
275
+ const emailTaken =
276
+ !checkout.isLoggedIn && checkout.existingAccountEmail === billing.email.trim()
277
+
278
+ const needsNationality = checkout.buyerFields.some((f) => f.kind === 'nationality')
279
+ const countriesQuery = hooks.useCountries({
280
+ enabled: needsNationality || gates.showAddress,
281
+ })
282
+ const countries = useMemo(() => countriesQuery.data ?? [], [countriesQuery.data])
283
+ const statesQuery = hooks.useStates(billing.countryId, {
284
+ enabled: Boolean(billing.countryId),
285
+ })
286
+ const states = useMemo(() => statesQuery.data ?? [], [statesQuery.data])
287
+
288
+ // Event country → signed-in profile → US, matching the hosted checkout's
289
+ // country preference order. Also retain the ISO code for Stripe + phone UI.
290
+ useEffect(() => {
291
+ if (
292
+ countryDefaultApplied.current ||
293
+ billing.countryId ||
294
+ countries.length === 0 ||
295
+ profile.isLoading ||
296
+ (eventId && event.isLoading)
297
+ ) {
298
+ return
299
+ }
300
+ const eventCode = event.data?.countryCode?.toLowerCase()
301
+ const preferred =
302
+ countries.find((country) => country.code?.toLowerCase() === eventCode) ??
303
+ countries.find(
304
+ (country) =>
305
+ profile.data?.countryId !== null &&
306
+ profile.data?.countryId !== undefined &&
307
+ String(country.id) === String(profile.data.countryId)
308
+ ) ??
309
+ countries.find((country) => country.code?.toLowerCase() === 'us')
310
+ if (!preferred) return
311
+ countryDefaultApplied.current = true
312
+ setBilling((current) => {
313
+ // The profile-seed effect above can run in this same commit, so the
314
+ // `billing` this effect closed over is stale — it still shows no country
315
+ // even though the seed just supplied one (with its state). Re-check the
316
+ // live value here: without it the default overwrote the seeded country
317
+ // and blanked `stateId`, which is why a returning buyer's State/County
318
+ // came back empty while every other saved field prefilled.
319
+ if (current.countryId) return current
320
+ return {
321
+ ...current,
322
+ countryId: String(preferred.id),
323
+ countryCode: preferred.code?.toUpperCase(),
324
+ stateId: undefined,
325
+ stateCode: undefined,
326
+ }
327
+ })
328
+ }, [
329
+ billing.countryId,
330
+ countries,
331
+ profile.isLoading,
332
+ profile.data?.countryId,
333
+ eventId,
334
+ event.isLoading,
335
+ event.data?.countryCode,
336
+ ])
337
+
338
+ useEffect(() => {
339
+ if (!billing.countryId) return
340
+ const country = countries.find(
341
+ (candidate) => String(candidate.id) === String(billing.countryId)
342
+ )
343
+ const countryCode = country?.code?.toUpperCase()
344
+ if (countryCode && countryCode !== billing.countryCode) {
345
+ setBilling((current) => ({ ...current, countryCode }))
346
+ }
347
+ }, [billing.countryId, billing.countryCode, countries])
348
+
349
+ useEffect(() => {
350
+ if (!billing.stateId) return
351
+ const state = states.find(
352
+ (candidate) => String(candidate.id) === String(billing.stateId)
353
+ )
354
+ const stateCode = state?.code
355
+ if (stateCode && stateCode !== billing.stateCode) {
356
+ setBilling((current) => ({ ...current, stateCode }))
357
+ }
358
+ }, [billing.stateId, billing.stateCode, states])
359
+
360
+ const setB = (k: keyof BillingDraft) => (e: ChangeEvent<HTMLInputElement>) =>
361
+ setBilling((b) => ({ ...b, [k]: e.target.value }))
362
+
363
+ const patchHolder = (i: number, patch: Partial<HolderDraft>) =>
364
+ setHolders((hs) => hs.map((h, j) => (j === i ? { ...h, ...patch } : h)))
365
+
366
+ const dobMax = useMemo(todayISO, [])
367
+ const [dobYear, dobMonth, dobDay] = dob ? dob.split('-').map(Number) : []
368
+ // A dial code on its own is not a phone number — see `isBlankPhone`.
369
+ const buyerPhone = isBlankPhone(billing.phone) ? undefined : billing.phone
370
+ const committedBilling: CheckoutBilling = {
371
+ ...checkout.billing,
372
+ ...billing,
373
+ phone: buyerPhone,
374
+ stateRequired: gates.showAddress && Boolean(billing.countryId) && states.length > 0,
375
+ dataCapture,
376
+ dobDay,
377
+ dobMonth,
378
+ dobYear,
379
+ }
380
+ const committedHolders: CheckoutHolder[] = holders.map((holder) => {
381
+ const holderPhone = isBlankPhone(holder.phone) ? '' : holder.phone
382
+ return {
383
+ firstName: holderNamesRequired
384
+ ? holder.firstName
385
+ : holder.firstName || billing.firstName,
386
+ lastName: holderNamesRequired
387
+ ? holder.lastName
388
+ : holder.lastName || billing.lastName,
389
+ email:
390
+ showHolders && collectHolderContact
391
+ ? holder.email
392
+ : holder.email || billing.email,
393
+ phone:
394
+ showHolders && collectHolderContact
395
+ ? holderPhone
396
+ : holderPhone || buyerPhone || '',
397
+ ticketTypeId: holder.ticketTypeId,
398
+ ticketDataCapture: holder.ticketDataCapture,
399
+ }
400
+ })
401
+ const missingDetails = missingCheckoutDetails(
402
+ {
403
+ billing: committedBilling,
404
+ holders: committedHolders,
405
+ selections: checkout.selections,
406
+ breakdown: checkout.breakdown,
407
+ holderNamesRequired: checkout.holderNamesRequired,
408
+ fieldGates: checkout.fieldGates,
409
+ customFields: checkout.customFields,
410
+ },
411
+ {
412
+ requireEmailConfirmation,
413
+ collectTicketHolders,
414
+ requireHolderContact: collectHolderContact,
415
+ }
416
+ )
417
+ const complete = missingDetails.length === 0
418
+ const showValidation = validation.attempt > 0
419
+ const missingSet = new Set(missingDetails)
420
+ const fieldError = (key: string, message = 'This field is required.') =>
421
+ showValidation && missingSet.has(key) ? message : undefined
422
+ const dobError = showValidation
423
+ ? missingDetails.find(
424
+ (detail) => detail === 'Date of birth' || detail.startsWith('Age ')
425
+ )
426
+ : undefined
427
+ const phoneCountry = (
428
+ billing.countryCode ??
429
+ event.data?.countryCode ??
430
+ 'US'
431
+ )?.toLowerCase() as CountryIso2
432
+
433
+ const commit = () => {
434
+ checkout.setBilling(committedBilling)
435
+ checkout.setHolders(committedHolders)
436
+ if (ttfOptInLabel || brandOptInLabel) checkout.setConsent(consent)
437
+ }
438
+
439
+ // Combined mode: keep the engine in sync as the form is filled, so <Payment/>
440
+ // is always chargeable without a separate "continue" click.
441
+ useEffect(() => {
442
+ if (combined) commit()
443
+ // eslint-disable-next-line react-hooks/exhaustive-deps
444
+ }, [combined, billing, holders, consent, dob, dataCapture])
445
+
446
+ const renderGroupFields = (
447
+ group: EventCustomFieldGroup,
448
+ values: CustomFieldValues,
449
+ onChange: (name: string, v: string | string[]) => void,
450
+ ticketTypeId?: string,
451
+ ticketIndex?: number
452
+ ) =>
453
+ ticketFieldsFor(group.fields, ticketTypeId).map((f) => {
454
+ const label = f.label || f.name
455
+ const missingKey =
456
+ ticketIndex === undefined ? label : `${label} (ticket ${ticketIndex + 1})`
457
+ return (
458
+ <CustomFieldInput
459
+ key={f.id}
460
+ field={f}
461
+ value={values[f.name]}
462
+ error={fieldError(missingKey)}
463
+ onChange={(v) => onChange(f.name, v)}
464
+ />
465
+ )
466
+ })
467
+
468
+ const hasEventSpecificDetails =
469
+ checkout.buyerFields.length > 0 ||
470
+ checkout.customFields.some((group) => group.fields.length > 0) ||
471
+ showHolders ||
472
+ gates.showDob
473
+ const skipBilling =
474
+ skipWhenProfileComplete &&
475
+ checkout.isLoggedIn &&
476
+ profile.isSuccess &&
477
+ complete &&
478
+ !hasEventSpecificDetails
479
+
480
+ // This is the safe EmbeddedCheckout interpretation of legacy
481
+ // `skip_billing_page`: payment remains visible; only a fully satisfied,
482
+ // ordinary billing card disappears.
483
+ if (skipBilling) return null
484
+
485
+ return (
486
+ <>
487
+ <Card className={className}>
488
+ <SectionHeader step={step} title={heading} />
489
+
490
+ {onLogin && !checkout.isLoggedIn && (
491
+ <div className="tf-checkout-billing__login">
492
+ <span>Login &amp; skip ahead:</span>
493
+ <button
494
+ type="button"
495
+ className="tf-checkout-billing__login-btn"
496
+ onClick={() => onLogin()}
497
+ >
498
+ Login
499
+ </button>
500
+ </div>
501
+ )}
502
+
503
+ <FieldRow>
504
+ <Field
505
+ error={fieldError('First name', 'First name is required.')}
506
+ invalid={Boolean(fieldError('First name'))}
507
+ >
508
+ <Input
509
+ placeholder="First name *"
510
+ value={billing.firstName}
511
+ required
512
+ invalid={Boolean(fieldError('First name'))}
513
+ aria-invalid={Boolean(fieldError('First name'))}
514
+ onChange={setB('firstName')}
515
+ />
516
+ </Field>
517
+ <Field
518
+ error={fieldError('Last name', 'Last name is required.')}
519
+ invalid={Boolean(fieldError('Last name'))}
520
+ >
521
+ <Input
522
+ placeholder="Last name *"
523
+ value={billing.lastName}
524
+ required
525
+ invalid={Boolean(fieldError('Last name'))}
526
+ aria-invalid={Boolean(fieldError('Last name'))}
527
+ onChange={setB('lastName')}
528
+ />
529
+ </Field>
530
+ </FieldRow>
531
+ <FieldRow>
532
+ <Field
533
+ error={fieldError('Valid email address', 'Enter a valid email address.')}
534
+ invalid={Boolean(fieldError('Valid email address'))}
535
+ >
536
+ <Input
537
+ type="email"
538
+ autoComplete="email"
539
+ placeholder="Email *"
540
+ value={billing.email}
541
+ required
542
+ invalid={Boolean(fieldError('Valid email address'))}
543
+ aria-invalid={Boolean(fieldError('Valid email address'))}
544
+ onChange={setB('email')}
545
+ />
546
+ </Field>
547
+ {requireEmailConfirmation && !checkout.isLoggedIn && (
548
+ <Field
549
+ error={fieldError(
550
+ 'Matching email confirmation',
551
+ 'Email addresses must match.'
552
+ )}
553
+ invalid={Boolean(fieldError('Matching email confirmation'))}
554
+ >
555
+ <Input
556
+ type="email"
557
+ autoComplete="email"
558
+ placeholder="Confirm email *"
559
+ value={billing.confirmEmail ?? ''}
560
+ required
561
+ invalid={Boolean(fieldError('Matching email confirmation'))}
562
+ aria-invalid={Boolean(fieldError('Matching email confirmation'))}
563
+ onChange={setB('confirmEmail')}
564
+ />
565
+ </Field>
566
+ )}
567
+ </FieldRow>
568
+ {!showValidation &&
569
+ requireEmailConfirmation &&
570
+ billing.confirmEmail &&
571
+ billing.confirmEmail.trim().toLowerCase() !==
572
+ billing.email.trim().toLowerCase() && (
573
+ <p className="tf-checkout-error-text">The email addresses do not match.</p>
574
+ )}
575
+ {emailTaken && (
576
+ <p className="tf-checkout-note tf-checkout-billing__email-taken">
577
+ This email is already registered.{' '}
578
+ {onLogin ? (
579
+ <button
580
+ type="button"
581
+ className="tf-checkout-billing__login-link"
582
+ onClick={() => onLogin(billing.email)}
583
+ >
584
+ Log in to autofill your details
585
+ </button>
586
+ ) : (
587
+ 'Log in to autofill your details.'
588
+ )}
589
+ </p>
590
+ )}
591
+
592
+ {checkout.buyerFields.map((f) => (
593
+ <BuyerField
594
+ key={f.key}
595
+ field={f}
596
+ value={String(billing[f.key] ?? '')}
597
+ onChange={(v) => setBilling((b) => ({ ...b, [f.key]: v }))}
598
+ countries={countries}
599
+ error={fieldError(
600
+ f.kind === 'nationality'
601
+ ? 'Nationality'
602
+ : f.kind === 'gender'
603
+ ? 'Gender'
604
+ : 'Instagram handle'
605
+ )}
606
+ />
607
+ ))}
608
+
609
+ {gates.showPhone && (
610
+ <Field
611
+ label={`Phone${gates.phoneRequired ? ' *' : ''}`}
612
+ error={fieldError('Valid phone number', 'Enter a valid phone number.')}
613
+ invalid={Boolean(fieldError('Valid phone number'))}
614
+ >
615
+ <PhoneInput
616
+ className="tf-checkout-phone"
617
+ inputClassName={`tf-checkout-input tf-checkout-phone__input${
618
+ fieldError('Valid phone number') ? ' tf-checkout-input--invalid' : ''
619
+ }`}
620
+ defaultCountry={phoneCountry}
621
+ // The flag owns the country; the input holds the national number
622
+ // only. Without `disableDialCodeAndPrefix` the empty field still
623
+ // expects a full international number, so a Mexican buyer typing
624
+ // "5512345678" has it read as a dial code and the country guess
625
+ // flips them to Brazil (+55) — submitting the wrong code.
626
+ disableDialCodeAndPrefix
627
+ showDisabledDialCodeAndPrefix
628
+ disableDialCodePrefill
629
+ value={billing.phone ?? ''}
630
+ required={gates.phoneRequired}
631
+ placeholder="Phone number"
632
+ inputProps={{
633
+ autoComplete: 'tel',
634
+ 'aria-label': 'Phone',
635
+ 'aria-invalid': Boolean(fieldError('Valid phone number')),
636
+ }}
637
+ onChange={(phone) => setBilling((current) => ({ ...current, phone }))}
638
+ />
639
+ </Field>
640
+ )}
641
+
642
+ {gates.showDob && (
643
+ <Field
644
+ label={`Date of birth *${
645
+ gates.minimumAge ? ` (must be ${gates.minimumAge}+)` : ''
646
+ }`}
647
+ error={
648
+ dobError === 'Date of birth'
649
+ ? 'Date of birth is required.'
650
+ : dobError
651
+ ? `You must be ${gates.minimumAge} or older.`
652
+ : undefined
653
+ }
654
+ invalid={Boolean(dobError)}
655
+ >
656
+ <Input
657
+ type="date"
658
+ value={dob}
659
+ required
660
+ // A date input with no bounds lets the browser's year segment run
661
+ // to six digits ("202555") and accept dates in the future. The
662
+ // range both caps the year at four digits and keeps the value a
663
+ // date someone could actually have been born on.
664
+ min={DOB_MIN}
665
+ max={dobMax}
666
+ invalid={Boolean(dobError)}
667
+ aria-invalid={Boolean(dobError)}
668
+ onChange={(e) => setDob(e.target.value)}
669
+ />
670
+ </Field>
671
+ )}
672
+
673
+ {gates.showAddress && (
674
+ <BillingAddressFields
675
+ billing={billing}
676
+ countries={countries}
677
+ states={states}
678
+ fieldError={fieldError}
679
+ setBilling={setBilling}
680
+ />
681
+ )}
682
+
683
+ {/* Order-level custom fields */}
684
+ {orderGroups.map((g) => (
685
+ <Fragment key={g.id}>
686
+ {g.label && (
687
+ <div className="tf-checkout-billing__section-label">{g.label}</div>
688
+ )}
689
+ {renderGroupFields(g, dataCapture, (name, v) =>
690
+ setDataCapture((d) => ({ ...d, [name]: v }))
691
+ )}
692
+ </Fragment>
693
+ ))}
694
+
695
+ {/* Marketing opt-ins (optional) */}
696
+ {brandOptInLabel && (
697
+ <Checkbox
698
+ checked={consent.brandOptIn}
699
+ onChange={(e) => setConsent((c) => ({ ...c, brandOptIn: e.target.checked }))}
700
+ className="tf-checkout-checkbox--consent"
701
+ label={brandOptInLabel}
702
+ />
703
+ )}
704
+ {privacyPolicy ? (
705
+ <div
706
+ className={
707
+ (showPrivacyError || showValidation) && !consent.ttfOptIn
708
+ ? 'tf-checkout-consent--error'
709
+ : ''
710
+ }
711
+ data-checkout-invalid={
712
+ ((showPrivacyError || showValidation) && !consent.ttfOptIn) || undefined
713
+ }
714
+ >
715
+ <Checkbox
716
+ required
717
+ checked={consent.ttfOptIn}
718
+ aria-invalid={(showPrivacyError || showValidation) && !consent.ttfOptIn}
719
+ onChange={(e) => setConsent((c) => ({ ...c, ttfOptIn: e.target.checked }))}
720
+ className="tf-checkout-checkbox--consent"
721
+ label={
722
+ <>
723
+ I have read and agree to {privacyPolicy.brandName}{' '}
724
+ <a href={privacyPolicy.url} target="_blank" rel="noreferrer">
725
+ {privacyPolicy.linkLabel || 'Privacy Policy.'}
726
+ </a>
727
+ {' *'}
728
+ </>
729
+ }
730
+ />
731
+ {(showPrivacyError || showValidation) && !consent.ttfOptIn && (
732
+ <div className="tf-checkout-consent__error">Required</div>
733
+ )}
734
+ </div>
735
+ ) : ttfOptInLabel ? (
736
+ <Checkbox
737
+ checked={consent.ttfOptIn}
738
+ onChange={(e) => setConsent((c) => ({ ...c, ttfOptIn: e.target.checked }))}
739
+ className="tf-checkout-checkbox--consent"
740
+ label={ttfOptInLabel}
741
+ />
742
+ ) : null}
743
+ {!combined && (
744
+ <>
745
+ <Button
746
+ block
747
+ onClick={() => {
748
+ if (!complete) {
749
+ validation.requestValidation()
750
+ return
751
+ }
752
+ commit()
753
+ onContinue?.()
754
+ }}
755
+ >
756
+ {buttonLabel}
757
+ </Button>
758
+ </>
759
+ )}
760
+ </Card>
761
+ {showHolders && (
762
+ <TicketHoldersCard
763
+ holders={holders}
764
+ holderNamesRequired={holderNamesRequired}
765
+ collectHolderContact={collectHolderContact}
766
+ phoneCountry={phoneCountry}
767
+ fieldError={fieldError}
768
+ patchHolder={patchHolder}
769
+ renderCustomFields={(holder, index) =>
770
+ ticketGroups.map((group) =>
771
+ renderGroupFields(
772
+ group,
773
+ holder.ticketDataCapture,
774
+ (name, value) =>
775
+ patchHolder(index, {
776
+ ticketDataCapture: {
777
+ ...holder.ticketDataCapture,
778
+ [name]: value,
779
+ },
780
+ }),
781
+ holder.ticketTypeId,
782
+ index
783
+ )
784
+ )
785
+ }
786
+ />
787
+ )}
788
+ </>
789
+ )
790
+ }