tf-checkout-react 1.0.42 → 1.0.46
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/api/index.d.ts +1 -0
- package/dist/components/billing-info-container/index.d.ts +22 -3
- package/dist/components/billing-info-container/utils.d.ts +2 -2
- package/dist/components/common/CheckboxField.d.ts +2 -2
- package/dist/components/confirmationContainer/index.d.ts +4 -1
- package/dist/components/loginModal/index.d.ts +8 -4
- package/dist/components/paymentContainer/index.d.ts +5 -1
- package/dist/components/registerModal/index.d.ts +3 -0
- package/dist/components/ticketsContainer/TicketRow.d.ts +10 -0
- package/dist/components/ticketsContainer/TicketsSection.d.ts +10 -0
- package/dist/components/ticketsContainer/index.d.ts +6 -1
- package/dist/components/ticketsContainer/utils.d.ts +4 -0
- package/dist/components/waitingList/index.d.ts +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/tf-checkout-react.cjs.development.css +3 -2
- package/dist/tf-checkout-react.cjs.development.js +688 -203
- 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 +693 -206
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/billing-info-data.d.ts +2 -1
- package/dist/validators/index.d.ts +2 -1
- package/package.json +3 -1
- package/src/.DS_Store +0 -0
- package/src/api/index.ts +5 -3
- package/src/components/.DS_Store +0 -0
- package/src/components/billing-info-container/index.tsx +239 -63
- package/src/components/billing-info-container/style.css +15 -0
- package/src/components/billing-info-container/utils.ts +41 -13
- package/src/components/common/CheckboxField.tsx +3 -2
- package/src/components/common/CustomField.tsx +16 -1
- package/src/components/confirmationContainer/index.tsx +8 -3
- package/src/components/loginModal/index.tsx +46 -13
- package/src/components/paymentContainer/index.tsx +10 -0
- package/src/components/registerModal/index.tsx +20 -3
- package/src/components/ticketsContainer/TicketRow.tsx +86 -0
- package/src/components/ticketsContainer/TicketsSection.tsx +82 -0
- package/src/components/ticketsContainer/index.tsx +96 -210
- package/src/components/ticketsContainer/utils.ts +11 -0
- package/src/components/waitingList/index.tsx +162 -0
- package/src/components/waitingList/style.css +18 -0
- package/src/index.ts +2 -1
- package/src/types/billing-info-data.ts +2 -1
- package/src/validators/index.ts +9 -3
|
@@ -1,151 +1,19 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react'
|
|
2
|
+
import axios, { AxiosError } from 'axios'
|
|
2
3
|
import './style.css'
|
|
3
4
|
|
|
4
5
|
import { getTickets, addToCart, setCustomHeader } from '../../api'
|
|
5
|
-
import _sortBy from 'lodash/sortBy'
|
|
6
6
|
import _get from 'lodash/get'
|
|
7
|
-
import _has from 'lodash/has'
|
|
8
7
|
import _some from 'lodash/some'
|
|
9
8
|
import _find from 'lodash/find'
|
|
10
9
|
import _isEmpty from 'lodash/isEmpty'
|
|
11
10
|
import _filter from 'lodash/filter'
|
|
12
11
|
import _isObject from 'lodash/isObject'
|
|
13
|
-
import Box from '@mui/material/Box'
|
|
14
|
-
import FormControl from '@mui/material/FormControl'
|
|
15
|
-
import MenuItem from '@mui/material/MenuItem'
|
|
16
|
-
import Select from '@mui/material/Select'
|
|
17
12
|
import CircularProgress from '@mui/material/CircularProgress'
|
|
18
13
|
import Button from 'react-bootstrap/Button'
|
|
19
14
|
import jwt_decode from 'jwt-decode'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
maxCount: number = 10,
|
|
23
|
-
minCount: number = 1,
|
|
24
|
-
multiplier: number = 1
|
|
25
|
-
) => {
|
|
26
|
-
const options = [{ label: 0, value: 0 }]
|
|
27
|
-
for (let i = minCount; i <= Math.min(50, maxCount); i += multiplier) {
|
|
28
|
-
options.push({ label: i, value: i })
|
|
29
|
-
}
|
|
30
|
-
return options
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const computeTierStateLabel = (
|
|
34
|
-
tier: any,
|
|
35
|
-
prevTier: any,
|
|
36
|
-
selectedTickets: any,
|
|
37
|
-
handleTicketSelect: any
|
|
38
|
-
) => {
|
|
39
|
-
const soldOutMessage = tier.soldOutMessage
|
|
40
|
-
? `${tier.soldOutMessage}`.toUpperCase()
|
|
41
|
-
: 'SOLD OUT'
|
|
42
|
-
const isSalesClosed =
|
|
43
|
-
!tier.salesStarted ||
|
|
44
|
-
tier.salesEnded ||
|
|
45
|
-
!_has(tier, 'maxQuantity') ||
|
|
46
|
-
tier.maxQuantity === 0
|
|
47
|
-
const options = getTicketSelectOptions(
|
|
48
|
-
tier.maxQuantity,
|
|
49
|
-
tier.minQuantity,
|
|
50
|
-
tier.multiplier
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
const onSaleContent = isSalesClosed ? null : (
|
|
54
|
-
<div className="get-tickets">
|
|
55
|
-
<Box className="get-tickets__selectbox">
|
|
56
|
-
<FormControl fullWidth>
|
|
57
|
-
<Select
|
|
58
|
-
value={selectedTickets[tier.id] ? selectedTickets[tier.id] : 0}
|
|
59
|
-
onChange={handleTicketSelect}
|
|
60
|
-
displayEmpty
|
|
61
|
-
inputProps={{ 'aria-label': 'Without label' }}
|
|
62
|
-
MenuProps={{
|
|
63
|
-
PaperProps: {
|
|
64
|
-
sx: { maxHeight: 150 },
|
|
65
|
-
className: 'get-tickets-paper',
|
|
66
|
-
},
|
|
67
|
-
}}
|
|
68
|
-
>
|
|
69
|
-
{options.map((option, index) => (
|
|
70
|
-
<MenuItem key={index} value={option.value}>
|
|
71
|
-
{option.value}
|
|
72
|
-
</MenuItem>
|
|
73
|
-
))}
|
|
74
|
-
</Select>
|
|
75
|
-
</FormControl>
|
|
76
|
-
</Box>
|
|
77
|
-
</div>
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
if (tier.sold_out || !tier.displayTicket || tier.soldOut)
|
|
81
|
-
return soldOutMessage
|
|
82
|
-
if (!+tier.cost && !+tier.price) return 'FREE'
|
|
83
|
-
if (tier.displayTicket) return onSaleContent
|
|
84
|
-
if (prevTier.in_stock) return 'SOON'
|
|
85
|
-
|
|
86
|
-
return ''
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const renderTiers = (
|
|
90
|
-
boxOffice: any,
|
|
91
|
-
selectedTickets: any,
|
|
92
|
-
handleTicketSelect: any,
|
|
93
|
-
promoCodeIsApplied: boolean
|
|
94
|
-
) => {
|
|
95
|
-
const sortedBoxOfiice = _sortBy(boxOffice, 'sortOrder')
|
|
96
|
-
const primaryTiers = sortedBoxOfiice.map((tier, i, arr) => {
|
|
97
|
-
const isSoldOut = tier.sold_out || !tier.displayTicket || tier.soldOut
|
|
98
|
-
const ticketSelect = (event: any) => {
|
|
99
|
-
const { value } = event.target
|
|
100
|
-
handleTicketSelect(tier.id, value)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let ticketIsDiscounted = false
|
|
104
|
-
if (
|
|
105
|
-
tier.oldPrice &&
|
|
106
|
-
promoCodeIsApplied &&
|
|
107
|
-
!isSoldOut &&
|
|
108
|
-
tier.oldPrice !== tier.price
|
|
109
|
-
) {
|
|
110
|
-
ticketIsDiscounted = true
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<div
|
|
115
|
-
key={tier.id || tier.name}
|
|
116
|
-
className={`event-detail__tier ${isSoldOut ? 'disabled' : ''}`}
|
|
117
|
-
>
|
|
118
|
-
<div className="event-detail__tier-name">
|
|
119
|
-
{tier.displayName || tier.name}
|
|
120
|
-
</div>
|
|
121
|
-
<div className="event-tickets-container">
|
|
122
|
-
<div className="event-detail__tier-price">
|
|
123
|
-
{ticketIsDiscounted && (
|
|
124
|
-
<p className="old-price">$ {(+tier.oldPrice).toFixed(2)}</p>
|
|
125
|
-
)}
|
|
126
|
-
<p>
|
|
127
|
-
{isSoldOut
|
|
128
|
-
? 'SOLD OUT'
|
|
129
|
-
: `$ ${(+tier.cost || +tier.price).toFixed(2)}`}
|
|
130
|
-
</p>
|
|
131
|
-
{!isSoldOut && (
|
|
132
|
-
<p className='fees'>{tier.taxesIncluded ? '(incl. Fees)' : '(excl. Fees)'}</p>
|
|
133
|
-
)}
|
|
134
|
-
</div>
|
|
135
|
-
<div className="event-detail__tier-state" style={{ minWidth: 55 }}>
|
|
136
|
-
{computeTierStateLabel(
|
|
137
|
-
tier,
|
|
138
|
-
arr[i - 1],
|
|
139
|
-
selectedTickets,
|
|
140
|
-
ticketSelect
|
|
141
|
-
)}
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
)
|
|
146
|
-
})
|
|
147
|
-
return primaryTiers
|
|
148
|
-
}
|
|
15
|
+
import { TicketsSection } from './TicketsSection'
|
|
16
|
+
import WaitingList from '../waitingList'
|
|
149
17
|
|
|
150
18
|
function Loader() {
|
|
151
19
|
return (
|
|
@@ -166,6 +34,11 @@ export interface IGetTickets {
|
|
|
166
34
|
onAddToCartSuccess: (response: CartSuccess) => void;
|
|
167
35
|
getTicketsLabel?: string;
|
|
168
36
|
contentStyle?: React.CSSProperties;
|
|
37
|
+
onAddToCartError: (e: AxiosError) => void;
|
|
38
|
+
onGetTicketsSuccess: (response: any) => void;
|
|
39
|
+
onGetTicketsError: (e: AxiosError) => void;
|
|
40
|
+
|
|
41
|
+
theme?: 'light' | 'dark';
|
|
169
42
|
}
|
|
170
43
|
|
|
171
44
|
export interface ITicket {
|
|
@@ -182,11 +55,16 @@ export const TicketsContainer = ({
|
|
|
182
55
|
eventId,
|
|
183
56
|
onAddToCartSuccess,
|
|
184
57
|
contentStyle = {},
|
|
58
|
+
onAddToCartError = () => {},
|
|
59
|
+
onGetTicketsSuccess = () => {},
|
|
60
|
+
onGetTicketsError = () => {},
|
|
61
|
+
theme = 'light',
|
|
185
62
|
}: IGetTickets) => {
|
|
186
63
|
const [selectedTickets, setSelectedTickets] = useState(
|
|
187
64
|
{} as ISelectedTickets
|
|
188
65
|
)
|
|
189
66
|
const [tickets, setTickets] = useState([] as ITicket[])
|
|
67
|
+
const [showWaitingList, setShowWaitingList] = useState(false)
|
|
190
68
|
const [isLoading, setIsLoading] = useState(false)
|
|
191
69
|
const [handleBookIsLoading, setHandleBookIsLoading] = useState(false)
|
|
192
70
|
const [promoCode, setPromoCode] = useState('')
|
|
@@ -216,14 +94,14 @@ export const TicketsContainer = ({
|
|
|
216
94
|
setCustomHeader(response)
|
|
217
95
|
const attributes = _get(response, 'data.data.attributes')
|
|
218
96
|
setPromoCodeIsApplied(attributes.ValidPromoCode)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
)
|
|
223
|
-
setTickets(tickets)
|
|
97
|
+
setTickets(_get(attributes, 'tickets'))
|
|
98
|
+
setShowWaitingList(attributes.showWaitingList)
|
|
99
|
+
onGetTicketsSuccess(response.data)
|
|
224
100
|
}
|
|
225
101
|
} catch (e) {
|
|
226
|
-
|
|
102
|
+
if (axios.isAxiosError(e)) {
|
|
103
|
+
onGetTicketsError(e)
|
|
104
|
+
}
|
|
227
105
|
} finally {
|
|
228
106
|
setIsLoading(false)
|
|
229
107
|
}
|
|
@@ -283,7 +161,9 @@ export const TicketsContainer = ({
|
|
|
283
161
|
})
|
|
284
162
|
}
|
|
285
163
|
} catch (e) {
|
|
286
|
-
|
|
164
|
+
if (axios.isAxiosError(e)) {
|
|
165
|
+
onAddToCartError(e)
|
|
166
|
+
}
|
|
287
167
|
} finally {
|
|
288
168
|
setHandleBookIsLoading(false)
|
|
289
169
|
}
|
|
@@ -295,80 +175,86 @@ export const TicketsContainer = ({
|
|
|
295
175
|
)
|
|
296
176
|
|
|
297
177
|
return (
|
|
298
|
-
<div className=
|
|
178
|
+
<div className={`get-tickets-page ${theme}`} style={contentStyle}>
|
|
299
179
|
{isLoading ? (
|
|
300
180
|
<Loader />
|
|
301
181
|
) : (
|
|
302
|
-
|
|
303
|
-
{
|
|
304
|
-
tickets
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
</div>
|
|
313
|
-
) : null}
|
|
314
|
-
{showPromoInput && (
|
|
315
|
-
<div className="promo-code-block">
|
|
316
|
-
<input
|
|
317
|
-
placeholder="Promo Code"
|
|
318
|
-
onChange={e => {
|
|
319
|
-
setPromoCode(e.target.value)
|
|
320
|
-
}}
|
|
321
|
-
onKeyPress={event => {
|
|
322
|
-
if (event.key === 'Enter') {
|
|
323
|
-
setPromoCodeUpdated(promoCode)
|
|
324
|
-
}
|
|
325
|
-
}}
|
|
182
|
+
<>
|
|
183
|
+
{showWaitingList ? (
|
|
184
|
+
<WaitingList tickets={tickets} />
|
|
185
|
+
) : (
|
|
186
|
+
<div>
|
|
187
|
+
<TicketsSection
|
|
188
|
+
ticketsList={tickets}
|
|
189
|
+
selectedTickets={selectedTickets}
|
|
190
|
+
handleTicketSelect={handleTicketSelect}
|
|
191
|
+
promoCodeIsApplied={promoCodeIsApplied}
|
|
326
192
|
/>
|
|
327
|
-
|
|
328
|
-
className="
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
193
|
+
{promoCodeIsApplied ? (
|
|
194
|
+
<div className="alert-info">
|
|
195
|
+
Your promo code was applied successfully.
|
|
196
|
+
</div>
|
|
197
|
+
) : null}
|
|
198
|
+
{showPromoInput && (
|
|
199
|
+
<div className="promo-code-block">
|
|
200
|
+
<input
|
|
201
|
+
placeholder="Promo Code"
|
|
202
|
+
onChange={e => {
|
|
203
|
+
setPromoCode(e.target.value)
|
|
204
|
+
}}
|
|
205
|
+
onKeyPress={event => {
|
|
206
|
+
if (event.key === 'Enter') {
|
|
207
|
+
setPromoCodeUpdated(promoCode)
|
|
208
|
+
}
|
|
209
|
+
}}
|
|
210
|
+
/>
|
|
211
|
+
<Button
|
|
212
|
+
className="promo-apply-button"
|
|
213
|
+
placeholder="Promo Code"
|
|
214
|
+
onClick={() => {
|
|
215
|
+
setPromoCodeUpdated(promoCode)
|
|
216
|
+
}}
|
|
217
|
+
>
|
|
218
|
+
Apply
|
|
219
|
+
</Button>
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
{!showPromoInput && !isAllTicketsSoldOut ? (
|
|
223
|
+
<Button
|
|
224
|
+
className="promo-code-button"
|
|
225
|
+
placeholder="Promo Codes"
|
|
226
|
+
onClick={() => {
|
|
227
|
+
setShowPromoInput(true)
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
Got a promo code? Click here
|
|
231
|
+
</Button>
|
|
232
|
+
) : null}
|
|
233
|
+
<div className="test v1.0.19" style={{ display: 'none' }} />
|
|
234
|
+
{!isAllTicketsSoldOut && (
|
|
235
|
+
<Button
|
|
236
|
+
aria-hidden={true}
|
|
237
|
+
className={`book-button ${
|
|
238
|
+
handleBookIsLoading ||
|
|
239
|
+
_isEmpty(selectedTickets) ||
|
|
240
|
+
Object.values(selectedTickets)[0] === 0
|
|
241
|
+
? 'disabled'
|
|
242
|
+
: ''
|
|
243
|
+
}`}
|
|
244
|
+
onClick={
|
|
245
|
+
!handleBookIsLoading &&
|
|
246
|
+
!_isEmpty(selectedTickets) &&
|
|
247
|
+
Object.values(selectedTickets)[0] > 0
|
|
248
|
+
? handleBook
|
|
249
|
+
: () => {}
|
|
250
|
+
}
|
|
251
|
+
>
|
|
252
|
+
{getTicketsLabel || 'GET TICKETS'}
|
|
253
|
+
</Button>
|
|
254
|
+
)}
|
|
336
255
|
</div>
|
|
337
256
|
)}
|
|
338
|
-
|
|
339
|
-
<Button
|
|
340
|
-
className="promo-code-button"
|
|
341
|
-
placeholder="Promo Codes"
|
|
342
|
-
onClick={() => {
|
|
343
|
-
setShowPromoInput(true)
|
|
344
|
-
}}
|
|
345
|
-
>
|
|
346
|
-
Got a promo code? Click here
|
|
347
|
-
</Button>
|
|
348
|
-
) : null}
|
|
349
|
-
<div className="test v1.0.19" style={{ display: 'none' }} />
|
|
350
|
-
{!isAllTicketsSoldOut && (
|
|
351
|
-
<Button
|
|
352
|
-
aria-hidden={true}
|
|
353
|
-
className={`book-button ${
|
|
354
|
-
handleBookIsLoading ||
|
|
355
|
-
_isEmpty(selectedTickets) ||
|
|
356
|
-
Object.values(selectedTickets)[0] === 0
|
|
357
|
-
? 'disabled'
|
|
358
|
-
: ''
|
|
359
|
-
}`}
|
|
360
|
-
onClick={
|
|
361
|
-
!handleBookIsLoading &&
|
|
362
|
-
!_isEmpty(selectedTickets) &&
|
|
363
|
-
Object.values(selectedTickets)[0] > 0
|
|
364
|
-
? handleBook
|
|
365
|
-
: () => {}
|
|
366
|
-
}
|
|
367
|
-
>
|
|
368
|
-
{getTicketsLabel || 'GET TICKETS'}
|
|
369
|
-
</Button>
|
|
370
|
-
)}
|
|
371
|
-
</div>
|
|
257
|
+
</>
|
|
372
258
|
)}
|
|
373
259
|
</div>
|
|
374
260
|
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const getTicketSelectOptions = (
|
|
2
|
+
maxCount: number = 10,
|
|
3
|
+
minCount: number = 1,
|
|
4
|
+
multiplier: number = 1
|
|
5
|
+
) => {
|
|
6
|
+
const options = [{ label: 0, value: 0 }]
|
|
7
|
+
for (let i = minCount; i <= Math.min(50, maxCount); i += multiplier) {
|
|
8
|
+
options.push({ label: i, value: i })
|
|
9
|
+
}
|
|
10
|
+
return options
|
|
11
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import Button from '@mui/material/Button'
|
|
3
|
+
import CircularProgress from '@mui/material/CircularProgress'
|
|
4
|
+
import { Field, Form, Formik } from 'formik'
|
|
5
|
+
import { CustomField } from '../common/CustomField'
|
|
6
|
+
import { addToWaitingList } from '../../api'
|
|
7
|
+
import {
|
|
8
|
+
combineValidators,
|
|
9
|
+
requiredValidator,
|
|
10
|
+
emailValidator,
|
|
11
|
+
} from '../../validators'
|
|
12
|
+
import { ENV } from '../../env'
|
|
13
|
+
|
|
14
|
+
import './style.css'
|
|
15
|
+
|
|
16
|
+
interface WaitingListProps {
|
|
17
|
+
tickets: any;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface WaitingListFields {
|
|
21
|
+
ticketTypeId: string;
|
|
22
|
+
quantity: string;
|
|
23
|
+
firstName: string;
|
|
24
|
+
lastName: string;
|
|
25
|
+
email: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const generateQuantity = (n: number) => {
|
|
29
|
+
const quantityList = []
|
|
30
|
+
for (let i = 1; i <= n; i++) {
|
|
31
|
+
quantityList.push({ label: i, value: i })
|
|
32
|
+
}
|
|
33
|
+
return quantityList
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
37
|
+
const [showSuccessMessage, setShowSuccessMessage] = useState(false)
|
|
38
|
+
const [loading, setLoading] = useState(false)
|
|
39
|
+
const ticketTypesList = Object.values(tickets).map((d: any) => ({
|
|
40
|
+
label: d.displayName,
|
|
41
|
+
value: d.id,
|
|
42
|
+
}))
|
|
43
|
+
|
|
44
|
+
const handleSubmit = async (values: WaitingListFields) => {
|
|
45
|
+
try {
|
|
46
|
+
setLoading(true)
|
|
47
|
+
const id = ENV.EVENT_ID
|
|
48
|
+
const requestData = {
|
|
49
|
+
data: {
|
|
50
|
+
attributes: values,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
const { data } = await addToWaitingList(id, requestData)
|
|
54
|
+
|
|
55
|
+
if (data.success) {
|
|
56
|
+
setShowSuccessMessage(true)
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.log(error)
|
|
60
|
+
} finally {
|
|
61
|
+
setLoading(false)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className="waiting-list">
|
|
67
|
+
{showSuccessMessage ? (
|
|
68
|
+
<div className="success-message">
|
|
69
|
+
<h3>{`You've been added to the waiting list!`}</h3>
|
|
70
|
+
<p>{`You'll be notified if tickets become available.`}</p>
|
|
71
|
+
</div>
|
|
72
|
+
) : (
|
|
73
|
+
<>
|
|
74
|
+
<h2>WAITING LIST</h2>
|
|
75
|
+
<Formik
|
|
76
|
+
initialValues={{
|
|
77
|
+
ticketTypeId: '',
|
|
78
|
+
quantity: '',
|
|
79
|
+
firstName: '',
|
|
80
|
+
lastName: '',
|
|
81
|
+
email: '',
|
|
82
|
+
}}
|
|
83
|
+
onSubmit={handleSubmit}
|
|
84
|
+
>
|
|
85
|
+
<Form>
|
|
86
|
+
<div className="field-item">
|
|
87
|
+
<Field
|
|
88
|
+
name="ticketTypeId"
|
|
89
|
+
label="Ticket types"
|
|
90
|
+
type="select"
|
|
91
|
+
component={CustomField}
|
|
92
|
+
selectOptions={[
|
|
93
|
+
{ label: 'Type of Ticket', value: '', disabled: true },
|
|
94
|
+
...ticketTypesList,
|
|
95
|
+
]}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="field-item">
|
|
99
|
+
<Field
|
|
100
|
+
name="quantity"
|
|
101
|
+
label="Quantity"
|
|
102
|
+
type="select"
|
|
103
|
+
component={CustomField}
|
|
104
|
+
selectOptions={[
|
|
105
|
+
{ label: 'Quantity Requested', value: '', disabled: true },
|
|
106
|
+
...generateQuantity(10),
|
|
107
|
+
]}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="field-item">
|
|
111
|
+
<Field
|
|
112
|
+
name="firstName"
|
|
113
|
+
label="First name"
|
|
114
|
+
validate={(value: string) =>
|
|
115
|
+
requiredValidator(value, 'Please enter your First name')
|
|
116
|
+
}
|
|
117
|
+
component={CustomField}
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
<div className="field-item">
|
|
121
|
+
<Field
|
|
122
|
+
name="lastName"
|
|
123
|
+
label="Last name"
|
|
124
|
+
validate={(value: string) =>
|
|
125
|
+
requiredValidator(value, 'Please enter your Last name')
|
|
126
|
+
}
|
|
127
|
+
component={CustomField}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
<div className="field-item">
|
|
131
|
+
<Field
|
|
132
|
+
name="email"
|
|
133
|
+
label="Email"
|
|
134
|
+
validate={combineValidators(
|
|
135
|
+
(value: string) =>
|
|
136
|
+
requiredValidator(value, 'Please enter your Email'),
|
|
137
|
+
(value: string) => emailValidator(value)
|
|
138
|
+
)}
|
|
139
|
+
component={CustomField}
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<Button
|
|
144
|
+
type="submit"
|
|
145
|
+
variant="contained"
|
|
146
|
+
className="waiting-list-button"
|
|
147
|
+
>
|
|
148
|
+
{loading ? (
|
|
149
|
+
<CircularProgress size="22px" />
|
|
150
|
+
) : (
|
|
151
|
+
'ADD TO WAITING LIST'
|
|
152
|
+
)}
|
|
153
|
+
</Button>
|
|
154
|
+
</Form>
|
|
155
|
+
</Formik>
|
|
156
|
+
</>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default WaitingList
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.waiting-list {
|
|
2
|
+
padding: 17px 35px 20px;
|
|
3
|
+
}
|
|
4
|
+
.waiting-list .field-item {
|
|
5
|
+
margin-bottom: 30px;
|
|
6
|
+
}
|
|
7
|
+
.waiting-list .waiting-list-button {
|
|
8
|
+
width: 100% !important;
|
|
9
|
+
}
|
|
10
|
+
.waiting-list .waiting-list-button:hover {
|
|
11
|
+
background-color: #505050;
|
|
12
|
+
}
|
|
13
|
+
.waiting-list .success-message h3 {
|
|
14
|
+
margin: 10px 0;
|
|
15
|
+
}
|
|
16
|
+
.waiting-list .success-message p {
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { BillingInfoContainer } from './components/billing-info-container/index'
|
|
|
2
2
|
export { PaymentContainer } from './components/paymentContainer/index'
|
|
3
3
|
export { ConfirmationContainer } from './components/confirmationContainer/index'
|
|
4
4
|
export { TicketsContainer } from './components/ticketsContainer/index'
|
|
5
|
-
export { currencyNormalizerCreator, createFixedFloatNormalizer } from './normalizers'
|
|
5
|
+
export { currencyNormalizerCreator, createFixedFloatNormalizer } from './normalizers'
|
|
6
|
+
export { LoginModal } from './components/loginModal'
|
|
@@ -9,7 +9,7 @@ export interface IGroupItem {
|
|
|
9
9
|
className?: string;
|
|
10
10
|
required?: boolean;
|
|
11
11
|
component?: ReactNode | JSX.Element | HTMLElement;
|
|
12
|
-
onValidate
|
|
12
|
+
onValidate?: (value: any) => void;
|
|
13
13
|
|
|
14
14
|
// aditional props
|
|
15
15
|
[key: string]: any;
|
|
@@ -22,6 +22,7 @@ export interface IFieldData {
|
|
|
22
22
|
groupClassname?: string;
|
|
23
23
|
groupLabel?: string | JSX.Element | HTMLElement;
|
|
24
24
|
groupLabelClassName?: string;
|
|
25
|
+
id: number;
|
|
25
26
|
}
|
|
26
27
|
export interface IBillingInfoData {
|
|
27
28
|
id: number | string;
|
package/src/validators/index.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
2
|
+
|
|
1
3
|
export const combineValidators = (...validators: any) => (...value: any) => {
|
|
2
4
|
for (let i = 0; i < validators.length; ++i) {
|
|
3
5
|
const error_message = validators[i](...value)
|
|
4
|
-
if (error_message
|
|
6
|
+
if (error_message) return error_message
|
|
5
7
|
}
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
export const requiredValidator = (value?: string | number): string => {
|
|
10
|
+
export const requiredValidator = (value?: string | number, message?: string): string => {
|
|
9
11
|
let errorMessage = ''
|
|
10
12
|
if (!value) {
|
|
11
|
-
errorMessage = 'Required'
|
|
13
|
+
errorMessage = message || 'Required'
|
|
12
14
|
}
|
|
13
15
|
return errorMessage
|
|
14
16
|
}
|
|
17
|
+
|
|
18
|
+
export const emailValidator = (email: string) => {
|
|
19
|
+
return !emailRegex.test(email) ? 'Please enter a valid email address' : ''
|
|
20
|
+
}
|