tf-checkout-react 1.0.47 → 1.0.51
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.
- package/dist/components/billing-info-container/index.d.ts +1 -1
- package/dist/components/billing-info-container/utils.d.ts +6 -0
- package/dist/components/common/CheckboxField.d.ts +2 -2
- package/dist/components/common/CustomField.d.ts +2 -1
- package/dist/components/common/SelectField.d.ts +20 -0
- package/dist/components/paymentContainer/index.d.ts +1 -1
- package/dist/components/stripePayment/index.d.ts +2 -1
- package/dist/tf-checkout-react.cjs.development.css +2 -2
- package/dist/tf-checkout-react.cjs.development.js +427 -264
- package/dist/tf-checkout-react.cjs.development.js.map +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
- package/dist/tf-checkout-react.esm.js +434 -271
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/billing-info-data.d.ts +3 -0
- package/dist/utils/formikErrorFocus.d.ts +2 -0
- package/dist/utils/getQueryVariable.d.ts +1 -0
- package/package.json +1 -1
- package/src/.DS_Store +0 -0
- package/src/components/.DS_Store +0 -0
- package/src/components/billing-info-container/index.tsx +124 -133
- package/src/components/billing-info-container/style.css +4 -0
- package/src/components/billing-info-container/utils.ts +44 -0
- package/src/components/common/CheckboxField.tsx +7 -2
- package/src/components/common/CustomField.tsx +21 -8
- package/src/components/common/SelectField.tsx +89 -0
- package/src/components/paymentContainer/index.tsx +31 -3
- package/src/components/stripePayment/index.tsx +48 -19
- package/src/components/stripePayment/style.css +10 -0
- package/src/components/waitingList/index.tsx +36 -25
- package/src/types/billing-info-data.ts +3 -0
- package/src/utils/formikErrorFocus.ts +24 -0
- package/src/utils/getQueryVariable.ts +13 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Select from '@mui/material/Select'
|
|
3
|
+
import _map from 'lodash/map'
|
|
4
|
+
import _get from 'lodash/get'
|
|
5
|
+
|
|
6
|
+
import { FieldInputProps, FormikProps } from 'formik'
|
|
7
|
+
import { makeStyles } from '@mui/styles'
|
|
8
|
+
import { FormControl, InputLabel, FormHelperText } from '@mui/material'
|
|
9
|
+
|
|
10
|
+
export interface ISelectOption {
|
|
11
|
+
label: string | number;
|
|
12
|
+
value?: string | number;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ISelectField {
|
|
17
|
+
label: string;
|
|
18
|
+
|
|
19
|
+
field: FieldInputProps<any>;
|
|
20
|
+
form: FormikProps<any>;
|
|
21
|
+
theme: 'dark' | 'light';
|
|
22
|
+
|
|
23
|
+
// optional
|
|
24
|
+
type?: string;
|
|
25
|
+
selectOptions?: ISelectOption[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface IOtherProps {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const useStyles = makeStyles({
|
|
33
|
+
input: {
|
|
34
|
+
'&::placeholder': {
|
|
35
|
+
color: 'gray',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export const SelectField = ({
|
|
41
|
+
label,
|
|
42
|
+
type = 'text',
|
|
43
|
+
field,
|
|
44
|
+
selectOptions = [] as ISelectOption[],
|
|
45
|
+
form: { touched, errors },
|
|
46
|
+
theme,
|
|
47
|
+
}: ISelectField & IOtherProps) => {
|
|
48
|
+
const isTouched = Boolean(_get(touched, field.name))
|
|
49
|
+
const error = _get(errors, field.name)
|
|
50
|
+
const classes = useStyles()
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<FormControl fullWidth={true}>
|
|
54
|
+
<InputLabel htmlFor={field.name} error={!!error && isTouched}>
|
|
55
|
+
{label}
|
|
56
|
+
</InputLabel>
|
|
57
|
+
<Select
|
|
58
|
+
id={field.name}
|
|
59
|
+
label={label}
|
|
60
|
+
type={type}
|
|
61
|
+
fullWidth={true}
|
|
62
|
+
error={!!error && isTouched}
|
|
63
|
+
inputProps={{
|
|
64
|
+
id: field.name,
|
|
65
|
+
classes: {
|
|
66
|
+
input: classes.input,
|
|
67
|
+
},
|
|
68
|
+
}}
|
|
69
|
+
native={true}
|
|
70
|
+
className={theme}
|
|
71
|
+
MenuProps={{ className: theme }}
|
|
72
|
+
{...field}
|
|
73
|
+
>
|
|
74
|
+
{_map(selectOptions, option => (
|
|
75
|
+
<option
|
|
76
|
+
key={option.value}
|
|
77
|
+
value={option.value}
|
|
78
|
+
disabled={option.disabled}
|
|
79
|
+
>
|
|
80
|
+
{option.label}
|
|
81
|
+
</option>
|
|
82
|
+
))}
|
|
83
|
+
</Select>
|
|
84
|
+
{isTouched && error ? (
|
|
85
|
+
<FormHelperText error={!!error && isTouched}>{error}</FormHelperText>
|
|
86
|
+
) : null}
|
|
87
|
+
</FormControl>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -8,12 +8,14 @@ import _map from 'lodash/map'
|
|
|
8
8
|
import _get from 'lodash/get'
|
|
9
9
|
import _identity from 'lodash/identity'
|
|
10
10
|
import { ENV } from '../../env'
|
|
11
|
+
import { nanoid } from 'nanoid'
|
|
12
|
+
import { getQueryVariable } from '../../utils/getQueryVariable'
|
|
11
13
|
|
|
12
14
|
import './style.css'
|
|
13
15
|
import StripePayment from '../stripePayment'
|
|
14
16
|
import { IOrderData, IPaymentField } from '../../types'
|
|
15
17
|
|
|
16
|
-
import { getPaymentData, handlePaymentSuccess } from '../../api'
|
|
18
|
+
import { getPaymentData, handlePaymentSuccess, getConditions } from '../../api'
|
|
17
19
|
|
|
18
20
|
const testId = ENV.STRIPE_PUBLISHABLE_KEY || 'pk_test_3Ov1P1oP33A1cxaSjxWE0VjT'
|
|
19
21
|
const stripePromise = loadStripe(
|
|
@@ -47,7 +49,7 @@ const initialReviewValues = {
|
|
|
47
49
|
payment_method: {
|
|
48
50
|
stripe_client_secret: '',
|
|
49
51
|
},
|
|
50
|
-
billing_info: {}
|
|
52
|
+
billing_info: {},
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
export const PaymentContainer = ({
|
|
@@ -59,16 +61,21 @@ export const PaymentContainer = ({
|
|
|
59
61
|
onErrorClose = _identity,
|
|
60
62
|
onGetPaymentDataSuccess = () => {},
|
|
61
63
|
onGetPaymentDataError = () => {},
|
|
62
|
-
onPaymentError = () => {}
|
|
64
|
+
onPaymentError = () => {},
|
|
63
65
|
}: IPaymentPage) => {
|
|
64
66
|
const [reviewData, setReviewData] = useState(initialReviewValues)
|
|
65
67
|
const [orderData, setOrderData] = useState(initialOrderValues)
|
|
66
68
|
const [error, setError] = useState(null)
|
|
67
69
|
const [paymentIsLoading, setPaymentIsLoading] = useState(false)
|
|
70
|
+
const [conditions, setConditions] = useState<{ id: string; text: string }[]>(
|
|
71
|
+
[]
|
|
72
|
+
)
|
|
68
73
|
|
|
69
74
|
const showFormTitle: boolean = Boolean(formTitle)
|
|
70
75
|
const showErrorText: boolean = Boolean(errorText)
|
|
71
76
|
|
|
77
|
+
const eventId = getQueryVariable('event_id')
|
|
78
|
+
|
|
72
79
|
useEffect(() => {
|
|
73
80
|
const { hash } = checkoutData;
|
|
74
81
|
(async () => {
|
|
@@ -99,6 +106,26 @@ export const PaymentContainer = ({
|
|
|
99
106
|
})()
|
|
100
107
|
}, [checkoutData])
|
|
101
108
|
|
|
109
|
+
//just once
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
// fetch conditions data
|
|
112
|
+
const fetchConditions = async () => {
|
|
113
|
+
if (eventId) {
|
|
114
|
+
const res = await getConditions(eventId)
|
|
115
|
+
const conditionsInfo = _get(res, 'data.data.attributes')
|
|
116
|
+
setConditions(
|
|
117
|
+
conditionsInfo
|
|
118
|
+
? conditionsInfo.map((item: string) => ({
|
|
119
|
+
id: nanoid(),
|
|
120
|
+
text: item,
|
|
121
|
+
}))
|
|
122
|
+
: []
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
fetchConditions()
|
|
127
|
+
}, [])
|
|
128
|
+
|
|
102
129
|
// 1. get payment data ---> v1/order/${hash}/review/ done
|
|
103
130
|
// 2. handle payment ---> v1/order/${orderHash}/pay/
|
|
104
131
|
// 3. redirect to confirmation page
|
|
@@ -163,6 +190,7 @@ export const PaymentContainer = ({
|
|
|
163
190
|
billing_info={reviewData.billing_info}
|
|
164
191
|
isLoading={paymentIsLoading}
|
|
165
192
|
handleSetLoading={value => setPaymentIsLoading(value)}
|
|
193
|
+
conditions={conditions}
|
|
166
194
|
/>
|
|
167
195
|
</Elements>
|
|
168
196
|
</div>
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { StripeCardNumberElementOptions } from '@stripe/stripe-js'
|
|
10
10
|
import _identity from 'lodash/identity'
|
|
11
11
|
import _get from 'lodash/get'
|
|
12
|
+
import { CheckboxField } from '../common/CheckboxField'
|
|
12
13
|
|
|
13
14
|
import './style.css'
|
|
14
15
|
import { getCurrencySymbolByCurrency } from '../../normalizers'
|
|
@@ -17,6 +18,7 @@ import CircularProgress from '@mui/material/CircularProgress'
|
|
|
17
18
|
const options: StripeCardNumberElementOptions = {
|
|
18
19
|
style: {
|
|
19
20
|
base: {
|
|
21
|
+
backgroundColor:'#232323',
|
|
20
22
|
fontSize: '18px',
|
|
21
23
|
color: '#ffffff',
|
|
22
24
|
letterSpacing: '1px',
|
|
@@ -42,13 +44,14 @@ export interface ICheckoutForm {
|
|
|
42
44
|
billing_info: { [key: string]: any };
|
|
43
45
|
isLoading: any;
|
|
44
46
|
handleSetLoading: (loading: any) => void;
|
|
47
|
+
conditions: any;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
interface AddressTypes {
|
|
48
|
-
city: string
|
|
49
|
-
line1: string
|
|
50
|
-
state: string
|
|
51
|
-
postal_code: string
|
|
51
|
+
city: string;
|
|
52
|
+
line1: string;
|
|
53
|
+
state: string;
|
|
54
|
+
postal_code: string;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
const CheckoutForm = ({
|
|
@@ -60,7 +63,8 @@ const CheckoutForm = ({
|
|
|
60
63
|
currency,
|
|
61
64
|
billing_info,
|
|
62
65
|
isLoading = false,
|
|
63
|
-
handleSetLoading = () => {}
|
|
66
|
+
handleSetLoading = () => {},
|
|
67
|
+
conditions = [],
|
|
64
68
|
}: ICheckoutForm) => {
|
|
65
69
|
const stripe = useStripe()
|
|
66
70
|
const elements = useElements()
|
|
@@ -71,9 +75,9 @@ const CheckoutForm = ({
|
|
|
71
75
|
handleSetLoading(true)
|
|
72
76
|
try {
|
|
73
77
|
event.preventDefault()
|
|
74
|
-
|
|
75
|
-
if(!postalCode) {
|
|
76
|
-
setStripeError(
|
|
78
|
+
|
|
79
|
+
if (!postalCode) {
|
|
80
|
+
setStripeError('Please enter your zip code.')
|
|
77
81
|
handleSetLoading(false)
|
|
78
82
|
return
|
|
79
83
|
}
|
|
@@ -84,7 +88,7 @@ const CheckoutForm = ({
|
|
|
84
88
|
handleSetLoading(false)
|
|
85
89
|
return
|
|
86
90
|
}
|
|
87
|
-
|
|
91
|
+
|
|
88
92
|
const card = elements.getElement(CardNumberElement)
|
|
89
93
|
const address: AddressTypes = {
|
|
90
94
|
city: billing_info.city,
|
|
@@ -100,7 +104,7 @@ const CheckoutForm = ({
|
|
|
100
104
|
address,
|
|
101
105
|
},
|
|
102
106
|
})
|
|
103
|
-
|
|
107
|
+
|
|
104
108
|
if (paymentMethodReq.error) {
|
|
105
109
|
setStripeError(paymentMethodReq.error.message || null)
|
|
106
110
|
handleSetLoading(false)
|
|
@@ -108,7 +112,7 @@ const CheckoutForm = ({
|
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
const { error } = await stripe.confirmCardPayment(stripe_client_secret, {
|
|
111
|
-
payment_method: paymentMethodReq.paymentMethod.id
|
|
115
|
+
payment_method: paymentMethodReq.paymentMethod.id,
|
|
112
116
|
})
|
|
113
117
|
|
|
114
118
|
if (error) {
|
|
@@ -116,26 +120,29 @@ const CheckoutForm = ({
|
|
|
116
120
|
handleSetLoading(false)
|
|
117
121
|
return
|
|
118
122
|
}
|
|
119
|
-
|
|
120
|
-
onSubmit(null)
|
|
121
123
|
|
|
122
|
-
|
|
124
|
+
onSubmit(null)
|
|
125
|
+
} catch (e) {
|
|
123
126
|
onSubmit(e)
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
const onChangePostalCode = (e: any) => {
|
|
128
131
|
setPostalCode(e.target.value)
|
|
129
|
-
}
|
|
132
|
+
}
|
|
130
133
|
|
|
131
134
|
useEffect(() => {
|
|
132
135
|
if (typeof window !== 'undefined') {
|
|
133
|
-
const userData = JSON.parse(
|
|
136
|
+
const userData = JSON.parse(
|
|
137
|
+
window.localStorage.getItem('user_data') || ''
|
|
138
|
+
)
|
|
134
139
|
const zipCode = _get(userData, 'zip', '')
|
|
135
140
|
zipCode && setPostalCode(zipCode)
|
|
136
141
|
}
|
|
137
142
|
}, [])
|
|
138
143
|
|
|
144
|
+
const buttonIsDiabled = !stripe || !!error || isLoading
|
|
145
|
+
|
|
139
146
|
return (
|
|
140
147
|
<div className="stripe_payment_container">
|
|
141
148
|
{!!stripeError && (
|
|
@@ -171,9 +178,31 @@ const CheckoutForm = ({
|
|
|
171
178
|
/>
|
|
172
179
|
</label>
|
|
173
180
|
</div>
|
|
174
|
-
|
|
175
|
-
<
|
|
176
|
-
{
|
|
181
|
+
{conditions?.map((checkbox: any) => (
|
|
182
|
+
<div
|
|
183
|
+
className={'billing-info-container__singleField'}
|
|
184
|
+
key={checkbox.id}
|
|
185
|
+
>
|
|
186
|
+
<div className="conditions-block">
|
|
187
|
+
<CheckboxField
|
|
188
|
+
name={checkbox.id}
|
|
189
|
+
label={checkbox.text}
|
|
190
|
+
required={true}
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
))}
|
|
195
|
+
<div
|
|
196
|
+
className={`payment_button ${
|
|
197
|
+
buttonIsDiabled ? 'disabled-payment-button' : ''
|
|
198
|
+
}`}
|
|
199
|
+
>
|
|
200
|
+
<button disabled={buttonIsDiabled} type="submit">
|
|
201
|
+
{isLoading ? (
|
|
202
|
+
<CircularProgress />
|
|
203
|
+
) : (
|
|
204
|
+
`Pay ${getCurrencySymbolByCurrency(currency)}${total}`
|
|
205
|
+
)}
|
|
177
206
|
</button>
|
|
178
207
|
</div>
|
|
179
208
|
</form>
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
background: #232323;
|
|
3
3
|
border-radius: 8px;
|
|
4
4
|
padding: 15px;
|
|
5
|
+
width: 50%;
|
|
6
|
+
margin: 0 auto;
|
|
7
|
+
min-width: 325px;
|
|
8
|
+
margin-bottom: 20px;
|
|
5
9
|
}
|
|
6
10
|
.card_form_inner .card_label_text {
|
|
7
11
|
color: #fff;
|
|
@@ -30,6 +34,12 @@
|
|
|
30
34
|
opacity: 0.7;
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
.disabled-payment-button button{
|
|
38
|
+
user-select: none;
|
|
39
|
+
pointer-events:none;
|
|
40
|
+
opacity: 0.3;
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
.checkout_error_block {
|
|
34
44
|
color: #e53935;
|
|
35
45
|
padding: 15px 0;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
emailValidator,
|
|
11
11
|
} from '../../validators'
|
|
12
12
|
import { ENV } from '../../env'
|
|
13
|
+
import { ErrorFocus } from '../../utils/formikErrorFocus'
|
|
13
14
|
|
|
14
15
|
import './style.css'
|
|
15
16
|
|
|
@@ -41,6 +42,8 @@ const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
|
41
42
|
value: d.id,
|
|
42
43
|
}))
|
|
43
44
|
|
|
45
|
+
const showTicketsField = Boolean(ticketTypesList.length)
|
|
46
|
+
|
|
44
47
|
const handleSubmit = async (values: WaitingListFields) => {
|
|
45
48
|
try {
|
|
46
49
|
setLoading(true)
|
|
@@ -56,7 +59,6 @@ const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
|
56
59
|
setShowSuccessMessage(true)
|
|
57
60
|
}
|
|
58
61
|
} catch (error) {
|
|
59
|
-
console.log(error)
|
|
60
62
|
} finally {
|
|
61
63
|
setLoading(false)
|
|
62
64
|
}
|
|
@@ -83,30 +85,39 @@ const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
|
83
85
|
onSubmit={handleSubmit}
|
|
84
86
|
>
|
|
85
87
|
<Form>
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
88
|
+
<ErrorFocus />
|
|
89
|
+
{showTicketsField && (
|
|
90
|
+
<>
|
|
91
|
+
<div className="field-item">
|
|
92
|
+
<Field
|
|
93
|
+
name="ticketTypeId"
|
|
94
|
+
label="Ticket types"
|
|
95
|
+
type="select"
|
|
96
|
+
component={CustomField}
|
|
97
|
+
selectOptions={[
|
|
98
|
+
{ label: 'Type of Ticket', value: '', disabled: true },
|
|
99
|
+
...ticketTypesList,
|
|
100
|
+
]}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="field-item">
|
|
104
|
+
<Field
|
|
105
|
+
name="quantity"
|
|
106
|
+
label="Quantity"
|
|
107
|
+
type="select"
|
|
108
|
+
component={CustomField}
|
|
109
|
+
selectOptions={[
|
|
110
|
+
{
|
|
111
|
+
label: 'Quantity Requested',
|
|
112
|
+
value: '',
|
|
113
|
+
disabled: true,
|
|
114
|
+
},
|
|
115
|
+
...generateQuantity(10),
|
|
116
|
+
]}
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
</>
|
|
120
|
+
)}
|
|
110
121
|
<div className="field-item">
|
|
111
122
|
<Field
|
|
112
123
|
name="firstName"
|
|
@@ -10,6 +10,7 @@ export interface IGroupItem {
|
|
|
10
10
|
required?: boolean;
|
|
11
11
|
component?: ReactNode | JSX.Element | HTMLElement;
|
|
12
12
|
onValidate?: (value: any) => void;
|
|
13
|
+
uniqueId?: string;
|
|
13
14
|
|
|
14
15
|
// aditional props
|
|
15
16
|
[key: string]: any;
|
|
@@ -23,6 +24,7 @@ export interface IFieldData {
|
|
|
23
24
|
groupLabel?: string | JSX.Element | HTMLElement;
|
|
24
25
|
groupLabelClassName?: string;
|
|
25
26
|
id: number;
|
|
27
|
+
uniqueId?: string;
|
|
26
28
|
}
|
|
27
29
|
export interface IBillingInfoData {
|
|
28
30
|
id: number | string;
|
|
@@ -31,4 +33,5 @@ export interface IBillingInfoData {
|
|
|
31
33
|
// optional
|
|
32
34
|
label?: string | JSX.Element;
|
|
33
35
|
labelClassName?: string;
|
|
36
|
+
uniqueId?: string;
|
|
34
37
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { connect, FormikContextType } from 'formik'
|
|
2
|
+
import { Component } from 'react'
|
|
3
|
+
|
|
4
|
+
interface IProps {
|
|
5
|
+
formik: FormikContextType<any>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class ErrorFocusInternal extends Component<IProps> {
|
|
9
|
+
public componentDidUpdate(prevProps: IProps) {
|
|
10
|
+
const { isSubmitting, isValidating, errors } = prevProps.formik
|
|
11
|
+
const keys = Object.keys(errors)
|
|
12
|
+
if (keys.length > 0 && isSubmitting && !isValidating) {
|
|
13
|
+
const selector = `[name="${keys[0]}"]`
|
|
14
|
+
const errorElement = document.querySelector(selector) as HTMLElement
|
|
15
|
+
if (errorElement) {
|
|
16
|
+
errorElement.focus()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public render = () => null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ErrorFocus = connect<{}>(ErrorFocusInternal)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const getQueryVariable = (variable: string) => {
|
|
2
|
+
if (typeof window !== 'undefined') {
|
|
3
|
+
const query = window.location.search.substring(1)
|
|
4
|
+
const vars = query.split('&')
|
|
5
|
+
for (let i = 0; i < vars.length; i++) {
|
|
6
|
+
const pair = vars[i].split('=')
|
|
7
|
+
if (pair[0] === variable) {
|
|
8
|
+
return pair[1]
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return false
|
|
13
|
+
}
|