tf-checkout-react 1.4.20 → 1.4.21

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 (59) hide show
  1. package/README.md +462 -229
  2. package/dist/api/guestTicketDelegation.d.ts +47 -0
  3. package/dist/api/index.d.ts +2 -2
  4. package/dist/components/common/CustomField.d.ts +1 -1
  5. package/dist/components/common/FieldSection/index.d.ts +19 -0
  6. package/dist/components/common/FieldSection/utils/index.d.ts +8 -0
  7. package/dist/components/common/index.d.ts +2 -2
  8. package/dist/components/confirmModal/index.d.ts +2 -2
  9. package/dist/components/delegationsContainer/IssueComponent.d.ts +10 -0
  10. package/dist/components/delegationsContainer/IssueTicketForm.d.ts +9 -0
  11. package/dist/components/delegationsContainer/TicketsAssignedTable.d.ts +11 -0
  12. package/dist/components/delegationsContainer/TicketsAvailableTable.d.ts +11 -0
  13. package/dist/components/delegationsContainer/index.d.ts +10 -0
  14. package/dist/components/idVerificationContainer/constants.d.ts +1 -0
  15. package/dist/components/index.d.ts +1 -0
  16. package/dist/components/loginForm/index.d.ts +45 -0
  17. package/dist/components/registerForm/adapters/index.d.ts +4 -0
  18. package/dist/components/registerForm/constants.d.ts +1 -0
  19. package/dist/components/registerForm/index.d.ts +16 -0
  20. package/dist/index.d.ts +3 -1
  21. package/dist/tf-checkout-react.cjs.development.js +1413 -283
  22. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  23. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  24. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  25. package/dist/tf-checkout-react.esm.js +1416 -288
  26. package/dist/tf-checkout-react.esm.js.map +1 -1
  27. package/dist/utils/form.d.ts +3 -0
  28. package/dist/utils/index.d.ts +3 -1
  29. package/dist/utils/replaceVarInString.d.ts +1 -0
  30. package/package.json +1 -1
  31. package/src/api/guestTicketDelegation.ts +79 -0
  32. package/src/api/index.ts +3 -2
  33. package/src/components/billing-info-container/utils.ts +1 -1
  34. package/src/components/common/CustomField.tsx +2 -0
  35. package/src/components/common/FieldSection/index.tsx +141 -0
  36. package/src/components/common/FieldSection/utils/index.tsx +92 -0
  37. package/src/components/common/SelectField/index.tsx +1 -1
  38. package/src/components/common/index.tsx +2 -2
  39. package/src/components/confirmModal/index.tsx +2 -2
  40. package/src/components/delegationsContainer/IssueComponent.tsx +155 -0
  41. package/src/components/delegationsContainer/IssueTicketForm.tsx +109 -0
  42. package/src/components/delegationsContainer/TicketsAssignedTable.tsx +55 -0
  43. package/src/components/delegationsContainer/TicketsAvailableTable.tsx +54 -0
  44. package/src/components/delegationsContainer/index.tsx +83 -0
  45. package/src/components/forgotPasswordModal/index.tsx +3 -9
  46. package/src/components/idVerificationContainer/constants.ts +5 -2
  47. package/src/components/index.ts +1 -0
  48. package/src/components/loginForm/index.tsx +195 -0
  49. package/src/components/registerForm/adapters/index.tsx +10 -0
  50. package/src/components/registerForm/constants.tsx +96 -0
  51. package/src/components/registerForm/index.tsx +192 -0
  52. package/src/index.ts +3 -4
  53. package/src/types/api/auth.d.ts +55 -0
  54. package/src/types/api/guestTicketDelegation.d.ts +18 -0
  55. package/src/types/formFields.d.ts +29 -0
  56. package/src/utils/form.ts +34 -0
  57. package/src/utils/index.ts +3 -1
  58. package/src/utils/replaceVarInString.ts +9 -0
  59. package/src/validators/index.ts +2 -2
@@ -0,0 +1,55 @@
1
+ import Table from '@mui/material/Table'
2
+ import TableBody from '@mui/material/TableBody'
3
+ import TableCell from '@mui/material/TableCell'
4
+ import TableContainer from '@mui/material/TableContainer'
5
+ import TableHead from '@mui/material/TableHead'
6
+ import TableRow from '@mui/material/TableRow'
7
+ import React from 'react'
8
+
9
+ export interface ITicketsAssignedTable {
10
+ tableTitle?: string;
11
+ classNamePrefix?: string;
12
+ noTicketsAssignedText?: string;
13
+ issuePageData: {
14
+ tickets: Array<any>;
15
+ };
16
+ }
17
+
18
+ const TicketsAssignedTable = ({
19
+ tableTitle = 'Tickets assigned',
20
+ classNamePrefix = 'delegations',
21
+ issuePageData,
22
+ noTicketsAssignedText = "You haven't issued any tickets yet.",
23
+ }: ITicketsAssignedTable) => (
24
+ <div className={`${classNamePrefix}-tables-block`}>
25
+ <div className={`${classNamePrefix}-ticket-holder`}>{tableTitle}</div>
26
+ {issuePageData.tickets?.length ? (
27
+ <TableContainer className="my-ticket-table">
28
+ <Table aria-label="collapsible table">
29
+ <TableHead>
30
+ <TableRow>
31
+ <TableCell key={'Ticket Holder'}>{'Ticket Holder'}</TableCell>
32
+ <TableCell key={'Ticket Type'}>{'Ticket Type'}</TableCell>
33
+ <TableCell key={'Status'}>{'Status'}</TableCell>
34
+ </TableRow>
35
+ </TableHead>
36
+ <TableBody>
37
+ {issuePageData.tickets?.map((ticket: any) => (
38
+ <TableRow key={ticket.id}>
39
+ <TableCell key={ticket.id + 'type'}>
40
+ {ticket.firstName + ' ' + ticket.lastName}
41
+ </TableCell>
42
+ <TableCell key={ticket.id + 'ticketType'}>{ticket.ticketType}</TableCell>
43
+ <TableCell key={ticket.id + 'status'}>{ticket.status}</TableCell>
44
+ </TableRow>
45
+ ))}
46
+ </TableBody>
47
+ </Table>
48
+ </TableContainer>
49
+ ) : (
50
+ noTicketsAssignedText
51
+ )}
52
+ </div>
53
+ )
54
+
55
+ export default TicketsAssignedTable
@@ -0,0 +1,54 @@
1
+ import Table from '@mui/material/Table'
2
+ import TableBody from '@mui/material/TableBody'
3
+ import TableCell from '@mui/material/TableCell'
4
+ import TableContainer from '@mui/material/TableContainer'
5
+ import TableHead from '@mui/material/TableHead'
6
+ import TableRow from '@mui/material/TableRow'
7
+ import _map from 'lodash/map'
8
+ import React from 'react'
9
+
10
+ import { IDelegationsTicketType } from '../../api/guestTicketDelegation'
11
+
12
+ export interface ITicketsAvailableTable {
13
+ tableTitle?: string;
14
+ classNamePrefix?: string;
15
+ issuePageData: {
16
+ ticketTypes: IDelegationsTicketType;
17
+ };
18
+ }
19
+
20
+ const TicketsAvailableTable = ({
21
+ tableTitle = 'Tickets available',
22
+ classNamePrefix = 'delegations',
23
+ issuePageData,
24
+ }: ITicketsAvailableTable) => (
25
+ <div className={`${classNamePrefix}-tables-block`}>
26
+ <div className={`${classNamePrefix}-ticket-holder`}>{tableTitle}</div>
27
+ <TableContainer className="my-ticket-table">
28
+ <Table aria-label="collapsible table">
29
+ <TableHead>
30
+ <TableRow>
31
+ <TableCell key={1}>{'Ticket Type'}</TableCell>
32
+ <TableCell key={2}>{'Total Quantity'}</TableCell>
33
+ <TableCell key={3}>{'Quantity Issued'}</TableCell>
34
+ <TableCell key={4}>{'Quantity Remaining'}</TableCell>
35
+ </TableRow>
36
+ </TableHead>
37
+ <TableBody>
38
+ {_map(issuePageData.ticketTypes, (val, key) => (
39
+ <TableRow key={key}>
40
+ <TableCell key={key + 'name'}>{val.optionValue}</TableCell>
41
+ <TableCell key={key + 'maxQty'}>{val.delegationMaxQuantity}</TableCell>
42
+ <TableCell key={key + 'issued'}>{val.delegationQuantityIssued}</TableCell>
43
+ <TableCell key={key + 'remaining'}>
44
+ {Number(val.delegationMaxQuantity) - Number(val.delegationQuantityIssued)}
45
+ </TableCell>
46
+ </TableRow>
47
+ ))}
48
+ </TableBody>
49
+ </Table>
50
+ </TableContainer>
51
+ </div>
52
+ )
53
+
54
+ export default TicketsAvailableTable
@@ -0,0 +1,83 @@
1
+ import _get from 'lodash/get'
2
+ import _identity from 'lodash/identity'
3
+ import React, { FC, useEffect, useState } from 'react'
4
+
5
+ import { getCustomerExistsData } from '../../api/guestTicketDelegation'
6
+ import { Loader } from '../../components/common'
7
+ import { LoginForm } from '../../components/loginForm'
8
+ import { RegistrationForm } from '../../components/registerForm'
9
+ import { useCookieListener } from '../../hooks/useCookieListener'
10
+ import { getCookieByName, getQueryVariable } from '../../utils'
11
+ import IssueComponent from './IssueComponent'
12
+
13
+ interface IRegistrationProps {
14
+ registerFormFields?: Array<IFormFieldsSection>;
15
+ classNamePrefix?: string;
16
+ logo?: string;
17
+ onCustomerExistsError?: (error: any) => void;
18
+ issuePageErrorText?: string;
19
+ }
20
+
21
+ const X_TF_ECOMMERCE = 'X-TF-ECOMMERCE'
22
+
23
+ export const DelegationsContainer: FC<IRegistrationProps> = ({
24
+ registerFormFields,
25
+ classNamePrefix = 'guest-ticket-delegation',
26
+ issuePageErrorText = 'You do not have access to manage these tickets.',
27
+ onCustomerExistsError = _identity,
28
+ logo,
29
+ }) => {
30
+ const [loading, setLoading] = useState(true)
31
+ const [isLoggedIn, setIsLoggedIn] = useState(Boolean(getCookieByName(X_TF_ECOMMERCE)))
32
+ const [isCustomerExsists, setIsCustomerExsists] = useState(false)
33
+ const [issuePageError, setIssuePageError] = useState(false)
34
+ const [customerEmail, setCustomerEmail] = useState('')
35
+ const accessHash = getQueryVariable('hash') || ''
36
+
37
+ useCookieListener(X_TF_ECOMMERCE, value => setIsLoggedIn(Boolean(value)))
38
+
39
+ const onGetIssuePageDataError = () => {
40
+ setIssuePageError(true)
41
+ }
42
+
43
+ useEffect(() => {
44
+ const getCustomerData = async () => {
45
+ try {
46
+ const response = await getCustomerExistsData(accessHash)
47
+ const isCustomerExsists = _get(response, 'data.attributes.customerExists', false)
48
+ setIsCustomerExsists(isCustomerExsists)
49
+ setCustomerEmail(_get(response, 'data.attributes.email', ''))
50
+ setLoading(false)
51
+ } catch (error) {
52
+ onCustomerExistsError(error)
53
+ }
54
+ }
55
+
56
+ getCustomerData()
57
+ }, [])
58
+
59
+ return (
60
+ <div className={`${classNamePrefix}__container`}>
61
+ {loading ? (
62
+ <Loader />
63
+ ) : issuePageError ? (
64
+ <div className="issue_page_error">{issuePageErrorText}</div>
65
+ ) : isLoggedIn ? (
66
+ <div>
67
+ <IssueComponent
68
+ classNamePrefix={classNamePrefix}
69
+ onGetIssuePageDataError={onGetIssuePageDataError}
70
+ />
71
+ </div>
72
+ ) : isCustomerExsists ? (
73
+ <LoginForm logo={logo} />
74
+ ) : (
75
+ <RegistrationForm
76
+ customerEmail={customerEmail}
77
+ formFields={registerFormFields}
78
+ registrationType="delegation"
79
+ />
80
+ )}
81
+ </div>
82
+ )
83
+ }
@@ -1,6 +1,6 @@
1
1
  import './style.css'
2
2
 
3
- import { Box, CircularProgress,Modal } from '@mui/material'
3
+ import { Box, CircularProgress, Modal } from '@mui/material'
4
4
  import axios, { AxiosError } from 'axios'
5
5
  import { Field, Form, Formik } from 'formik'
6
6
  import React, { FC, useState } from 'react'
@@ -35,9 +35,7 @@ const style: React.CSSProperties = {
35
35
  }
36
36
 
37
37
  const Schema = Yup.object().shape({
38
- email: Yup.string()
39
- .email('Invalid email')
40
- .required('Required'),
38
+ email: Yup.string().email('Invalid email').required('Required'),
41
39
  })
42
40
 
43
41
  export const ForgotPasswordModal: FC<IForgotPasswordProps> = ({
@@ -87,11 +85,7 @@ export const ForgotPasswordModal: FC<IForgotPasswordProps> = ({
87
85
  <div className="forgot-password-container">
88
86
  <div className="title">Password Reset</div>
89
87
  <div className="forgot-password-container__singleField">
90
- <Field
91
- name="email"
92
- label="Email"
93
- component={CustomField}
94
- />
88
+ <Field name="email" label="Email" component={CustomField} />
95
89
  </div>
96
90
  </div>
97
91
  <div className="forgot-password-action-button">
@@ -12,8 +12,11 @@ export const TRANSACTION_STATUSES = {
12
12
  }
13
13
 
14
14
  export const VERIFICATION_MESSAGES = {
15
- PENDING: 'Your ID verification is currently being processed. We will notify you as soon as it is completed. Thank you for your patience.',
15
+ PENDING:
16
+ 'Your ID verification is currently being processed. We will notify you as soon as it is completed. Thank you for your patience.',
16
17
  APPROVED: 'Your ID verification is approved!',
17
18
  FAILED: 'Unfortunately your ID verification has failed. Please try again.',
18
- WRONG_CUSTOMER: 'The order does not belong to the customer.'
19
+ WRONG_CUSTOMER: 'The order does not belong to the customer.',
19
20
  }
21
+
22
+ export const DELEGATION_ACCESS_ERROR = 'Delegation Access not found'
@@ -7,5 +7,6 @@ export { OrderDetailsContainer } from './orderDetailsContainer'
7
7
  export { ResetPasswordContainer } from './resetPasswordContainer'
8
8
  export { TicketResaleContainer } from './ticketResale'
9
9
  export { AddonsContainter } from './addonsContainer'
10
+ export { DelegationsContainer } from './delegationsContainer'
10
11
  export { SeatMapContainer } from './seatMapContainer'
11
12
  export { IDVerification } from './idVerificationContainer'
@@ -0,0 +1,195 @@
1
+ import axios, { AxiosError } from 'axios'
2
+ import { Field, Form, Formik } from 'formik'
3
+ import _get from 'lodash/get'
4
+ import _identity from 'lodash/identity'
5
+ import React, { FC, useState } from 'react'
6
+
7
+ import { authorize, getProfileData } from '../../api'
8
+ import {
9
+ combineValidators,
10
+ emailValidator,
11
+ requiredValidator,
12
+ } from '../../validators'
13
+ import { CustomField } from '../common'
14
+
15
+ export interface ILoginFormProps {
16
+ alreadyHasUser?: boolean;
17
+ userExpired?: boolean;
18
+
19
+ onLoginSuccess?: (res: IProfileData) => void;
20
+ onLoginError?: (e: AxiosError) => void;
21
+
22
+ onGetProfileDataSuccess?: (res: IProfileData) => void;
23
+ onGetProfileDataError?: (e: AxiosError) => void;
24
+
25
+ onForgotPasswordButtonClick?: () => void;
26
+ onSignupButtonClick?: () => void;
27
+
28
+ logo?: string;
29
+ showForgotPasswordButton?: boolean;
30
+ showSignUpButton?: boolean;
31
+ }
32
+
33
+ interface IUserData {
34
+ id: string;
35
+ firstName: string;
36
+ lastName: string;
37
+ email: string;
38
+ city?: string;
39
+ country?: string;
40
+ countryId?: string;
41
+ phone?: string;
42
+ streetAddress?: string;
43
+ state?: string;
44
+ zip?: string;
45
+ zipCode?: string;
46
+ stateId?: string;
47
+ }
48
+
49
+ export const setLoggedUserData = (data: IUserData) => ({
50
+ id: data.id,
51
+ first_name: data.firstName,
52
+ last_name: data.lastName,
53
+ email: data.email,
54
+ confirmEmail: data.email,
55
+ city: data?.city || '',
56
+ country: data?.countryId || data?.country || '',
57
+ phone: data?.phone || '',
58
+ street_address: data?.streetAddress || '',
59
+ state: data?.stateId || '',
60
+ zip: data?.zip || data?.zipCode || '',
61
+ })
62
+
63
+ export const LoginForm: FC<ILoginFormProps> = ({
64
+ alreadyHasUser = false,
65
+ userExpired = false,
66
+
67
+ onLoginSuccess = _identity,
68
+ onLoginError = _identity,
69
+
70
+ onGetProfileDataSuccess = _identity,
71
+ onGetProfileDataError = _identity,
72
+
73
+ onForgotPasswordButtonClick = _identity,
74
+ onSignupButtonClick = _identity,
75
+
76
+ logo,
77
+ showForgotPasswordButton = false,
78
+ showSignUpButton = false,
79
+ }) => {
80
+ const [error, setError] = useState('')
81
+ return (
82
+ <div className='login-modal'>
83
+ <Formik
84
+ initialValues={{ email: '', password: '' }}
85
+ onSubmit={async ({ email, password }) => {
86
+ try {
87
+ const body = { email, password }
88
+ const authRes = await authorize(body)
89
+ let profileResponse = null
90
+ try {
91
+ profileResponse = await getProfileData()
92
+ onGetProfileDataSuccess(_get(profileResponse, 'data.data'))
93
+ } catch (e) {
94
+ if (axios.isAxiosError(e)) {
95
+ onGetProfileDataError(e)
96
+ }
97
+ return
98
+ }
99
+
100
+ const profileSpecifiedData = _get(profileResponse, 'data.data')
101
+ const profileDataObj = setLoggedUserData(profileSpecifiedData)
102
+ if (typeof window !== 'undefined') {
103
+ window.localStorage.setItem(
104
+ 'user_data',
105
+ JSON.stringify(profileDataObj)
106
+ )
107
+ const event = new window.CustomEvent('tf-login')
108
+ window.document.dispatchEvent(event)
109
+ }
110
+ onLoginSuccess(_get(authRes, 'data.data'))
111
+ } catch (e) {
112
+ if (axios.isAxiosError(e)) {
113
+ const error = e?.response?.data?.message || 'Error'
114
+ setError(error)
115
+ onLoginError(e)
116
+ } else if (e instanceof Error) {
117
+ setError(e?.message || 'Error')
118
+ }
119
+ }
120
+ }}
121
+ >
122
+ {props => (
123
+ <Form onSubmit={props.handleSubmit}>
124
+ <div className="modal-title">Login</div>
125
+ <div className="login-logo-container">
126
+ <img
127
+ className="login-logo-tff"
128
+ src={
129
+ logo ||
130
+ 'https://www.ticketfairy.com/resources/images/logo-ttf-black.svg'
131
+ }
132
+ alt="logo"
133
+ />
134
+ </div>
135
+ <div className="server_auth__error">{error}</div>
136
+ {alreadyHasUser && (
137
+ <p className="info-text-for-login">
138
+ It appears this email is already attached to an account. Please
139
+ log in here to complete your registration.
140
+ </p>
141
+ )}
142
+ {userExpired && (
143
+ <p className="info-text-for-login">
144
+ Your session has expired, please log in again.
145
+ </p>
146
+ )}
147
+ <div className="login-modal-body">
148
+ <div className="login-modal-body__email">
149
+ <Field
150
+ name="email"
151
+ label="Email"
152
+ type="email"
153
+ component={CustomField}
154
+ validate={combineValidators(
155
+ requiredValidator,
156
+ emailValidator
157
+ )}
158
+ />
159
+ </div>
160
+ <div className="login-modal-body__password">
161
+ <Field
162
+ name="password"
163
+ label="Password"
164
+ type="password"
165
+ component={CustomField}
166
+ validate={requiredValidator}
167
+ />
168
+ </div>
169
+ <div className="login-action-button">
170
+ <button type="submit">Login</button>
171
+ </div>
172
+ {showForgotPasswordButton && (
173
+ <div className="forgot-password">
174
+ <span
175
+ aria-hidden="true"
176
+ onClick={onForgotPasswordButtonClick}
177
+ >
178
+ Forgot password?
179
+ </span>
180
+ </div>
181
+ )}
182
+ {showSignUpButton && (
183
+ <div className="forgot-password">
184
+ <span aria-hidden="true" onClick={onSignupButtonClick}>
185
+ Sign up
186
+ </span>
187
+ </div>
188
+ )}
189
+ </div>
190
+ </Form>
191
+ )}
192
+ </Formik>
193
+ </div>
194
+ )
195
+ }
@@ -0,0 +1,10 @@
1
+ import _map from 'lodash/map'
2
+
3
+ export const collectStates = (states: any) => {
4
+ const mappedStates = _map(states, (item, key) => ({
5
+ label: item,
6
+ value: key,
7
+ }))
8
+
9
+ return mappedStates
10
+ }
@@ -0,0 +1,96 @@
1
+ import React from 'react'
2
+
3
+ export const formDefaultFields: IFormFieldsSection[] = [
4
+ {
5
+ name: 'basic-info',
6
+ groupLabel: 'Create your account.',
7
+ groupLabelClassName: '',
8
+ groupClassName: '',
9
+ fields: [
10
+ {
11
+ className: 'half-width',
12
+ name: 'firstName',
13
+ label: 'First Name',
14
+ type: 'text',
15
+ required: true,
16
+ onValidate: null,
17
+ },
18
+ {
19
+ className: 'half-width',
20
+ name: 'lastName',
21
+ label: 'Last Name',
22
+ type: 'text',
23
+ required: true,
24
+ onValidate: null,
25
+ },
26
+ {
27
+ name: 'email',
28
+ label: 'Email',
29
+ type: 'email',
30
+ required: true,
31
+ onValidate: null,
32
+ },
33
+ {
34
+ name: 'confirmEmail',
35
+ label: 'Confirm Email',
36
+ type: 'email',
37
+ required: true,
38
+ onValidate: null,
39
+ },
40
+ ],
41
+ },
42
+ {
43
+ name: 'billing-info',
44
+ groupLabel: '',
45
+ groupLabelClassName: '',
46
+ groupClassName: '',
47
+ fields: [
48
+ {
49
+ className: 'half-width',
50
+ name: 'zip',
51
+ label: 'Post Code/Zip',
52
+ type: 'text',
53
+ required: true,
54
+ onValidate: null,
55
+ },
56
+ {
57
+ className: 'half-width',
58
+ name: 'country',
59
+ label: 'Country',
60
+ type: 'select',
61
+ required: true,
62
+ onValidate: null,
63
+ },
64
+ ],
65
+ },
66
+ {
67
+ name: 'password-info',
68
+ groupLabel: (
69
+ <div className="email-info-block">
70
+ <span>Choose a password for your new</span>
71
+ <b> Mana Common </b>
72
+ <span>account</span>
73
+ </div>
74
+ ),
75
+ groupLabelClassName: '',
76
+ groupClassName: '',
77
+ fields: [
78
+ {
79
+ className: 'half-width',
80
+ name: 'password',
81
+ label: 'Password',
82
+ type: 'password',
83
+ required: true,
84
+ onValidate: null,
85
+ },
86
+ {
87
+ className: 'half-width',
88
+ name: 'confirmPassword',
89
+ label: 'Confirm Password',
90
+ type: 'password',
91
+ required: true,
92
+ onValidate: null,
93
+ },
94
+ ],
95
+ },
96
+ ]