tf-checkout-react 1.3.51 → 1.4.0
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 +6 -1
- package/dist/components/common/Loader.d.ts +1 -1
- package/dist/components/idVerificationContainer/constants.d.ts +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/seatMapContainer/SeatMapComponent.d.ts +8 -0
- package/dist/components/seatMapContainer/TicketsSection.d.ts +9 -0
- package/dist/components/seatMapContainer/addToCart.d.ts +21 -0
- package/dist/components/seatMapContainer/index.d.ts +2 -0
- package/dist/components/seatMapContainer/utils.d.ts +14 -0
- package/dist/components/stripePayment/index.d.ts +2 -2
- package/dist/components/ticketsContainer/TicketRow.d.ts +3 -1
- package/dist/components/ticketsContainer/TicketsSection.d.ts +5 -1
- package/dist/components/ticketsContainer/index.d.ts +6 -2
- package/dist/index.d.ts +1 -0
- package/dist/tf-checkout-react.cjs.development.js +1435 -128
- 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 +1436 -130
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/tf-checkout-styles.css +1 -1
- package/dist/types/order-data.d.ts +3 -0
- package/dist/utils/createCheckoutDataBodyWithDefaultHolder.d.ts +9 -2
- package/package.json +12 -4
- package/src/.d.ts +4 -3
- package/src/api/index.ts +89 -6
- package/src/components/billing-info-container/index.tsx +111 -102
- package/src/components/common/Loader.tsx +6 -8
- package/src/components/common/dist/PhoneNumberField.js +96 -0
- package/src/components/confirmationContainer/index.tsx +11 -9
- package/src/components/idVerificationContainer/constants.ts +2 -0
- package/src/components/idVerificationContainer/index.tsx +54 -13
- package/src/components/index.ts +2 -1
- package/src/components/orderDetailsContainer/index.tsx +54 -23
- package/src/components/paymentContainer/index.tsx +167 -33
- package/src/components/seatMapContainer/SeatMapComponent.tsx +73 -0
- package/src/components/seatMapContainer/TicketsSection.tsx +254 -0
- package/src/components/seatMapContainer/addToCart.ts +82 -0
- package/src/components/seatMapContainer/index.tsx +408 -0
- package/src/components/seatMapContainer/utils.ts +138 -0
- package/src/components/stripePayment/index.tsx +23 -18
- package/src/components/ticketsContainer/TicketRow.tsx +28 -13
- package/src/components/ticketsContainer/TicketsSection.tsx +85 -2
- package/src/components/ticketsContainer/index.tsx +57 -12
- package/src/components/ticketsContainer/style.css +0 -3
- package/src/hooks/usePixel.ts +35 -1
- package/src/index.ts +2 -1
- package/src/types/order-data.ts +3 -0
- package/src/types/seatMap.d.ts +154 -0
- package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +6 -2
|
@@ -56,7 +56,6 @@ const getStripePromise = (reviewData: any) => {
|
|
|
56
56
|
|
|
57
57
|
return loadStripe(stripePublishableKey, options)
|
|
58
58
|
}
|
|
59
|
-
|
|
60
59
|
export interface IPaymentPage {
|
|
61
60
|
paymentFields: IPaymentField[];
|
|
62
61
|
handlePayment: any;
|
|
@@ -83,14 +82,18 @@ export interface IPaymentPage {
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
const initialOrderValues: IOrderData = {
|
|
85
|
+
id: '',
|
|
86
86
|
product_name: '',
|
|
87
87
|
ticketType: '',
|
|
88
88
|
quantity: '',
|
|
89
89
|
price: '',
|
|
90
90
|
total: '',
|
|
91
91
|
currency: '',
|
|
92
|
+
guest_count: '',
|
|
93
|
+
pay_now: '',
|
|
92
94
|
add_ons: [] as IAddOn[],
|
|
93
95
|
}
|
|
96
|
+
|
|
94
97
|
const initialReviewValues = {
|
|
95
98
|
order_details: {
|
|
96
99
|
order_hash: '',
|
|
@@ -138,7 +141,8 @@ export const PaymentContainer = ({
|
|
|
138
141
|
const eventId =
|
|
139
142
|
getQueryVariable('event_id') || _get(reviewData, 'cart[0].product_id') || ''
|
|
140
143
|
const { hash, total } = checkoutData
|
|
141
|
-
const isFreeTickets =
|
|
144
|
+
const isFreeTickets =
|
|
145
|
+
(!Number(total) && !Number(orderData.total)) || !Number(orderData.pay_now)
|
|
142
146
|
|
|
143
147
|
const pageUrl = isBrowser ? window.location.href.split('?')[0] : ''
|
|
144
148
|
usePixel(eventId, { page: 'review', pageUrl })
|
|
@@ -154,7 +158,18 @@ export const PaymentContainer = ({
|
|
|
154
158
|
const {
|
|
155
159
|
tickets: [ticket],
|
|
156
160
|
} = order_details
|
|
161
|
+
|
|
162
|
+
const orderDataArray = _map(order_details.tickets, item => ({
|
|
163
|
+
product_name: cart[0]?.product_name,
|
|
164
|
+
ticketType: item?.name,
|
|
165
|
+
quantity: item?.guest_count,
|
|
166
|
+
price: item?.price,
|
|
167
|
+
id: item.id,
|
|
168
|
+
count: item?.quantity,
|
|
169
|
+
}))
|
|
170
|
+
|
|
157
171
|
const orderData = {
|
|
172
|
+
id: order_details?.id,
|
|
158
173
|
product_name: cart[0]?.product_name,
|
|
159
174
|
ticketType: ticket?.name,
|
|
160
175
|
quantity: ticket?.quantity,
|
|
@@ -162,6 +177,10 @@ export const PaymentContainer = ({
|
|
|
162
177
|
total: order_details?.total,
|
|
163
178
|
currency: order_details?.currency,
|
|
164
179
|
add_ons: order_details?.add_ons || [],
|
|
180
|
+
pay_now: order_details?.pay_now || '',
|
|
181
|
+
guest_count: order_details?.guest_count || null,
|
|
182
|
+
debt: order_details?.debt || null,
|
|
183
|
+
tableTypes: orderDataArray,
|
|
165
184
|
}
|
|
166
185
|
setOrderData(orderData)
|
|
167
186
|
onGetPaymentDataSuccess(response.data)
|
|
@@ -213,6 +232,22 @@ export const PaymentContainer = ({
|
|
|
213
232
|
if (paymentSuccessResponse.status === 200) {
|
|
214
233
|
handlePayment(paymentSuccessResponse)
|
|
215
234
|
setPaymentIsLoading(false)
|
|
235
|
+
|
|
236
|
+
// clear seat-map related data from localStorage
|
|
237
|
+
localStorage.removeItem('reservationData')
|
|
238
|
+
localStorage.removeItem(`reservationStart-${eventId}`)
|
|
239
|
+
localStorage.removeItem('ownReservations')
|
|
240
|
+
localStorage.removeItem('tierId')
|
|
241
|
+
|
|
242
|
+
const isWindowDefined = typeof window !== "undefined"
|
|
243
|
+
if (isWindowDefined) {
|
|
244
|
+
(window as any)?.dataLayer.push({
|
|
245
|
+
'event': 'Purchase',
|
|
246
|
+
'orderValue': orderData.total,
|
|
247
|
+
'orderCurrency': orderData.currency,
|
|
248
|
+
'orderId': orderData.id
|
|
249
|
+
})
|
|
250
|
+
}
|
|
216
251
|
}
|
|
217
252
|
} catch (e) {
|
|
218
253
|
setError(_get(e, 'response.data.message'))
|
|
@@ -222,7 +257,47 @@ export const PaymentContainer = ({
|
|
|
222
257
|
}
|
|
223
258
|
|
|
224
259
|
const themeMui = createTheme(themeOptions)
|
|
225
|
-
|
|
260
|
+
const hasTableTypes = Boolean(Number(orderData.guest_count))
|
|
261
|
+
const paymentFieldsData = hasTableTypes
|
|
262
|
+
? [
|
|
263
|
+
{
|
|
264
|
+
label: 'Event',
|
|
265
|
+
id: 'product_name',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
label: '',
|
|
269
|
+
id: 'tableTypes',
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
label: 'Total (incl. fees, card processing and taxes)',
|
|
273
|
+
id: 'total',
|
|
274
|
+
normalizer: (value: string, currency: any) =>
|
|
275
|
+
currencyNormalizerCreator(
|
|
276
|
+
createFixedFloatNormalizer(2)(parseFloat(value)),
|
|
277
|
+
currency
|
|
278
|
+
),
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
label: 'Pay Now',
|
|
282
|
+
id: 'pay_now',
|
|
283
|
+
normalizer: (value: string, currency: any) =>
|
|
284
|
+
currencyNormalizerCreator(
|
|
285
|
+
createFixedFloatNormalizer(2)(parseFloat(value)),
|
|
286
|
+
currency
|
|
287
|
+
),
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
label: 'Pay On Check-in',
|
|
291
|
+
id: 'debt',
|
|
292
|
+
normalizer: (value: string, currency: any) =>
|
|
293
|
+
currencyNormalizerCreator(
|
|
294
|
+
createFixedFloatNormalizer(2)(parseFloat(value)),
|
|
295
|
+
currency
|
|
296
|
+
),
|
|
297
|
+
},
|
|
298
|
+
]
|
|
299
|
+
: paymentFields
|
|
300
|
+
const isTable = orderData?.guest_count
|
|
226
301
|
return (
|
|
227
302
|
<ThemeProvider theme={themeMui}>
|
|
228
303
|
<div className="payment_page">
|
|
@@ -241,10 +316,15 @@ export const PaymentContainer = ({
|
|
|
241
316
|
{paymentDataIsLoading && <Loader />}
|
|
242
317
|
{!paymentDataIsLoading && (
|
|
243
318
|
<Container maxWidth="md">
|
|
244
|
-
{showFormTitle &&
|
|
319
|
+
{showFormTitle && (
|
|
320
|
+
<h1>{isTable ? 'Get Your Tables' : formTitle}</h1>
|
|
321
|
+
)}
|
|
245
322
|
<div className="order_info_text">{orderInfoLabel}</div>
|
|
246
|
-
<div
|
|
247
|
-
|
|
323
|
+
<div
|
|
324
|
+
className="order_info_section"
|
|
325
|
+
style={{ display: hasTableTypes ? 'block' : 'grid' }}
|
|
326
|
+
>
|
|
327
|
+
{_map(paymentFieldsData, field => {
|
|
248
328
|
const {
|
|
249
329
|
id,
|
|
250
330
|
label,
|
|
@@ -253,39 +333,89 @@ export const PaymentContainer = ({
|
|
|
253
333
|
} = field
|
|
254
334
|
const { currency } = orderData
|
|
255
335
|
const value = orderData[id as keyof IOrderData]
|
|
336
|
+
let component = null
|
|
256
337
|
|
|
257
338
|
if (field.id === 'add_ons' && _isEmpty(value)) {
|
|
258
339
|
return false
|
|
259
340
|
}
|
|
260
341
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
342
|
+
if (field.id === 'tableTypes') {
|
|
343
|
+
const valueArray = value as Array<any>
|
|
344
|
+
|
|
345
|
+
component = (
|
|
346
|
+
<div
|
|
347
|
+
key={id}
|
|
348
|
+
className="order_info_block"
|
|
349
|
+
style={{
|
|
350
|
+
display: 'flex',
|
|
351
|
+
flexDirection: 'column',
|
|
352
|
+
}}
|
|
353
|
+
>
|
|
354
|
+
{_map(valueArray, tableTypeItem => (
|
|
355
|
+
<div
|
|
356
|
+
key={tableTypeItem.id}
|
|
357
|
+
style={{
|
|
358
|
+
display: 'grid',
|
|
359
|
+
gridTemplateColumns: '33% 33% 33%',
|
|
360
|
+
gridColumnGap: '10%',
|
|
361
|
+
}}
|
|
362
|
+
>
|
|
363
|
+
<div className="order_info_block">
|
|
364
|
+
<div className="order_info_title">Table Type</div>
|
|
365
|
+
<div className={`${className} order_info_text`}>
|
|
366
|
+
{tableTypeItem.ticketType}
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
<div className="order_info_block">
|
|
370
|
+
<div className="order_info_title">
|
|
371
|
+
Number of Tables
|
|
372
|
+
</div>
|
|
373
|
+
<div className={`${className} order_info_text`}>
|
|
374
|
+
{tableTypeItem.count}
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
<div className="order_info_block">
|
|
378
|
+
<div className="order_info_title">Guest Count</div>
|
|
379
|
+
<div className={`${className} order_info_text`}>
|
|
380
|
+
{tableTypeItem.quantity}
|
|
285
381
|
</div>
|
|
286
|
-
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
))}
|
|
385
|
+
</div>
|
|
386
|
+
)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return (
|
|
390
|
+
component || (
|
|
391
|
+
<div key={id} className="order_info_block">
|
|
392
|
+
<div className="order_info_title">{label}</div>
|
|
393
|
+
<div className={`${className} order_info_text`}>
|
|
394
|
+
{typeof value === 'string'
|
|
395
|
+
? normalizer(value, currency)
|
|
396
|
+
: _map(value, item => (
|
|
397
|
+
<div key={item.id} className="add-on-container">
|
|
398
|
+
<span>{item.quantity}</span>
|
|
399
|
+
<span className="add-on-x">{' x '}</span>
|
|
400
|
+
<span>
|
|
401
|
+
{item.groupName ? item.groupName + ' - ' : ''}
|
|
402
|
+
</span>
|
|
403
|
+
<span>{item.name}</span>
|
|
404
|
+
<span>{' - '}</span>
|
|
405
|
+
<span>
|
|
406
|
+
{currencyNormalizerCreator(
|
|
407
|
+
createFixedFloatNormalizer(2)(
|
|
408
|
+
parseFloat(item.price)
|
|
409
|
+
),
|
|
410
|
+
currency
|
|
411
|
+
)}
|
|
412
|
+
</span>
|
|
413
|
+
<span className="add-on-each">{' each'}</span>
|
|
414
|
+
</div>
|
|
415
|
+
))}
|
|
416
|
+
</div>
|
|
287
417
|
</div>
|
|
288
|
-
|
|
418
|
+
)
|
|
289
419
|
)
|
|
290
420
|
})}
|
|
291
421
|
</div>
|
|
@@ -354,7 +484,11 @@ export const PaymentContainer = ({
|
|
|
354
484
|
reviewData,
|
|
355
485
|
'payment_method.stripe_client_secret'
|
|
356
486
|
)}
|
|
357
|
-
total={
|
|
487
|
+
total={
|
|
488
|
+
orderData.guest_count
|
|
489
|
+
? orderData.pay_now
|
|
490
|
+
: orderData.total
|
|
491
|
+
}
|
|
358
492
|
onSubmit={handlePaymentMiddleWare}
|
|
359
493
|
error={error}
|
|
360
494
|
currency={orderData.currency}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import 'tf-seat-map-view/dist/index.css'
|
|
2
|
+
|
|
3
|
+
import React, { useEffect } from 'react'
|
|
4
|
+
import ReactDom from 'react-dom'
|
|
5
|
+
import SeatMapView from 'tf-seat-map-view'
|
|
6
|
+
|
|
7
|
+
import { getTierRelationsArray } from './utils'
|
|
8
|
+
|
|
9
|
+
const CONTAINER_DEFAULT_ID = 'seat_map_default_container'
|
|
10
|
+
|
|
11
|
+
// Temp solution
|
|
12
|
+
declare global {
|
|
13
|
+
interface Window {
|
|
14
|
+
tierPrices: any;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const SeatMapComponent = (props: ISeatMapContainerProps) => {
|
|
19
|
+
const { seatMapProps, mapContainerId } = props
|
|
20
|
+
const {
|
|
21
|
+
seatData,
|
|
22
|
+
statuses,
|
|
23
|
+
seatMapType = null,
|
|
24
|
+
seatMapEvents = {},
|
|
25
|
+
ticketTypeTierRelations = {},
|
|
26
|
+
tierPrices,
|
|
27
|
+
isReserving,
|
|
28
|
+
predefinedSeats,
|
|
29
|
+
} = seatMapProps
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const parentElement = document.getElementById(
|
|
33
|
+
mapContainerId || CONTAINER_DEFAULT_ID
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
// Temp solution
|
|
37
|
+
window.tierPrices = tierPrices
|
|
38
|
+
|
|
39
|
+
if (Boolean(parentElement)) {
|
|
40
|
+
const mapComponent = (
|
|
41
|
+
<SeatMapView
|
|
42
|
+
disabled={isReserving}
|
|
43
|
+
loading={isReserving}
|
|
44
|
+
events={seatMapEvents}
|
|
45
|
+
isSelectionOn={false}
|
|
46
|
+
seatData={seatData}
|
|
47
|
+
statuses={statuses}
|
|
48
|
+
ticketTypeTireRelationsArray={getTierRelationsArray(
|
|
49
|
+
ticketTypeTierRelations
|
|
50
|
+
)}
|
|
51
|
+
width={Math.min(parentElement?.clientWidth || 800, 800)}
|
|
52
|
+
height={Math.min(parentElement?.clientWidth || 800, 800)}
|
|
53
|
+
isBlockMap={seatMapType === 'block'}
|
|
54
|
+
isTableMap={seatMapType === 'table'}
|
|
55
|
+
predefinedSeats={predefinedSeats}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
ReactDom.render(mapComponent, parentElement)
|
|
60
|
+
}
|
|
61
|
+
}, [
|
|
62
|
+
isReserving,
|
|
63
|
+
mapContainerId,
|
|
64
|
+
seatData,
|
|
65
|
+
seatMapEvents,
|
|
66
|
+
seatMapType,
|
|
67
|
+
tierPrices,
|
|
68
|
+
statuses,
|
|
69
|
+
ticketTypeTierRelations,
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
return <div id={CONTAINER_DEFAULT_ID} />
|
|
73
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { CSSProperties } from '@emotion/serialize'
|
|
2
|
+
import { createTheme, Select, SelectChangeEvent,ThemeOptions } from '@mui/material'
|
|
3
|
+
import CircularProgress from '@mui/material/CircularProgress'
|
|
4
|
+
import FormControl from '@mui/material/FormControl'
|
|
5
|
+
import InputLabel from '@mui/material/InputLabel'
|
|
6
|
+
import MenuItem from '@mui/material/MenuItem'
|
|
7
|
+
import { ThemeProvider } from '@mui/private-theming'
|
|
8
|
+
import _find from 'lodash/find'
|
|
9
|
+
import _identity from 'lodash/identity'
|
|
10
|
+
import _isEmpty from 'lodash/isEmpty'
|
|
11
|
+
import _keys from 'lodash/keys'
|
|
12
|
+
import _map from 'lodash/map'
|
|
13
|
+
import _some from 'lodash/some'
|
|
14
|
+
import React from 'react'
|
|
15
|
+
import { Button } from 'react-bootstrap'
|
|
16
|
+
|
|
17
|
+
import { createFixedFloatNormalizer } from '../../normalizers'
|
|
18
|
+
import { getButtonLabel, getTicketDropdownData } from './utils'
|
|
19
|
+
|
|
20
|
+
export const TicketsSection = (
|
|
21
|
+
props: ITicketsSectionProps & {
|
|
22
|
+
themeOptions?: ThemeOptions & {
|
|
23
|
+
input?: CSSProperties;
|
|
24
|
+
checkbox?: CSSProperties;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
) => {
|
|
28
|
+
const {
|
|
29
|
+
ticketTypeTierRelations,
|
|
30
|
+
reservedSeats,
|
|
31
|
+
theme = 'light',
|
|
32
|
+
getTicketsBtnLabel,
|
|
33
|
+
contentStyle = {},
|
|
34
|
+
isButtonScrollable = false,
|
|
35
|
+
themeOptions,
|
|
36
|
+
handleGetTicketClick,
|
|
37
|
+
handleCancelReservation,
|
|
38
|
+
ticketDeleteButtonContent = 'Delete',
|
|
39
|
+
selectedTickets,
|
|
40
|
+
handleTicketSelect,
|
|
41
|
+
currencySymbol,
|
|
42
|
+
tableMapEnabled = false,
|
|
43
|
+
guestCounts,
|
|
44
|
+
setGuestCounts,
|
|
45
|
+
isAddingToCart,
|
|
46
|
+
} = props
|
|
47
|
+
|
|
48
|
+
const bookButtonIsDisabled =
|
|
49
|
+
_some(selectedTickets, item => !item) ||
|
|
50
|
+
_isEmpty(reservedSeats) ||
|
|
51
|
+
reservedSeats.length !== _keys(selectedTickets).length
|
|
52
|
+
const themeMui = createTheme(themeOptions)
|
|
53
|
+
|
|
54
|
+
const ticketsDropdownsData = getTicketDropdownData(
|
|
55
|
+
reservedSeats,
|
|
56
|
+
ticketTypeTierRelations
|
|
57
|
+
)
|
|
58
|
+
const handleTicketChange = (event: SelectChangeEvent<string>, seatId: string) => {
|
|
59
|
+
const {
|
|
60
|
+
target: { value },
|
|
61
|
+
} = event
|
|
62
|
+
|
|
63
|
+
if (value !== 'default') {
|
|
64
|
+
handleTicketSelect(value, seatId)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<ThemeProvider theme={themeMui}>
|
|
70
|
+
<div className={`get-tickets-page ${theme}`} style={contentStyle}>
|
|
71
|
+
<div className="tickets-section">
|
|
72
|
+
{_map(selectedTickets, (ticketItem: string, key: string) => {
|
|
73
|
+
const dropdownData =
|
|
74
|
+
_find(ticketsDropdownsData, item => item.seatId === key) ||
|
|
75
|
+
({} as ITicketsDropdownsData)
|
|
76
|
+
|
|
77
|
+
const selectedTicketData = _find(
|
|
78
|
+
dropdownData.ticketsData,
|
|
79
|
+
ticket => ticket.ticket_type_id === ticketItem
|
|
80
|
+
) as TicketTypeTierRelationsData
|
|
81
|
+
|
|
82
|
+
// guest count dropdown options
|
|
83
|
+
const startNum = Number(
|
|
84
|
+
selectedTicketData?.ticket_type_min_number_of_guests
|
|
85
|
+
)
|
|
86
|
+
const endNum = Number(
|
|
87
|
+
selectedTicketData?.ticket_type_max_number_of_guests
|
|
88
|
+
)
|
|
89
|
+
const numLength = endNum - startNum + 1
|
|
90
|
+
const showGuestCountDropdown = Boolean(startNum && endNum)
|
|
91
|
+
|
|
92
|
+
// prices
|
|
93
|
+
const guestPrice =
|
|
94
|
+
(guestCounts[dropdownData.seatId] - startNum) *
|
|
95
|
+
Number(selectedTicketData?.guest_price)
|
|
96
|
+
|
|
97
|
+
const finalPrice = createFixedFloatNormalizer(2)(
|
|
98
|
+
selectedTicketData?.ticket_type_price + guestPrice
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div className="ticket" key={key}>
|
|
103
|
+
<div className="ticketsDropdowns">
|
|
104
|
+
<Select
|
|
105
|
+
value={ticketItem || 'default'}
|
|
106
|
+
onChange={event =>
|
|
107
|
+
handleTicketChange(event, dropdownData.seatId)
|
|
108
|
+
}
|
|
109
|
+
inputProps={{ 'aria-label': 'Without label' }}
|
|
110
|
+
MenuProps={{
|
|
111
|
+
PaperProps: {
|
|
112
|
+
sx: { maxHeight: 150 },
|
|
113
|
+
className: 'get-tickets-paper',
|
|
114
|
+
},
|
|
115
|
+
}}
|
|
116
|
+
displayEmpty
|
|
117
|
+
sx={{ borderRadius: 0 }}
|
|
118
|
+
>
|
|
119
|
+
<MenuItem value={'default'}>
|
|
120
|
+
{`Please select ${
|
|
121
|
+
tableMapEnabled ? 'Table Type' : 'Ticket Type'
|
|
122
|
+
}`}
|
|
123
|
+
</MenuItem>
|
|
124
|
+
{_map(dropdownData.ticketsData, (option, index) => {
|
|
125
|
+
if (option.ticket_type_id !== 'default') {
|
|
126
|
+
return (
|
|
127
|
+
<MenuItem value={option.ticket_type_id} key={index}>
|
|
128
|
+
{option.ticket_type_name}
|
|
129
|
+
</MenuItem>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
return null
|
|
133
|
+
})}
|
|
134
|
+
</Select>
|
|
135
|
+
<Button
|
|
136
|
+
className="ticket-delete"
|
|
137
|
+
onClick={() => {
|
|
138
|
+
handleCancelReservation(
|
|
139
|
+
dropdownData.seatId,
|
|
140
|
+
dropdownData.tierId
|
|
141
|
+
)
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
{ticketDeleteButtonContent}
|
|
145
|
+
</Button>
|
|
146
|
+
</div>
|
|
147
|
+
{selectedTicketData && (
|
|
148
|
+
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
|
149
|
+
<div
|
|
150
|
+
style={{
|
|
151
|
+
width: '100%',
|
|
152
|
+
display: 'flex',
|
|
153
|
+
flexDirection: 'column',
|
|
154
|
+
justifyContent: 'center',
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
<div className="ticketPrice">
|
|
158
|
+
Price:
|
|
159
|
+
<span className="ticketPrice-value">
|
|
160
|
+
{`${currencySymbol} ${finalPrice}`}
|
|
161
|
+
</span>
|
|
162
|
+
</div>
|
|
163
|
+
{!_isEmpty(selectedTicketData.ticket_type_deposit) && (
|
|
164
|
+
<div className="ticketDeposit">
|
|
165
|
+
Deposit:
|
|
166
|
+
<span className="ticketPrice-value">
|
|
167
|
+
{` ${selectedTicketData?.ticket_type_deposit}%`}
|
|
168
|
+
</span>
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
{showGuestCountDropdown && (
|
|
173
|
+
<div
|
|
174
|
+
style={{
|
|
175
|
+
width: '100%',
|
|
176
|
+
display: 'flex',
|
|
177
|
+
justifyContent: 'end',
|
|
178
|
+
marginRight: 16,
|
|
179
|
+
}}
|
|
180
|
+
>
|
|
181
|
+
<FormControl
|
|
182
|
+
variant="outlined"
|
|
183
|
+
sx={{ m: 1, minWidth: 80 }}
|
|
184
|
+
>
|
|
185
|
+
<InputLabel
|
|
186
|
+
id="demo-simple-select-standard-label"
|
|
187
|
+
shrink
|
|
188
|
+
>
|
|
189
|
+
GUESTS
|
|
190
|
+
</InputLabel>
|
|
191
|
+
<Select
|
|
192
|
+
label="GUESTS"
|
|
193
|
+
labelId="demo-simple-select-standard-label"
|
|
194
|
+
value={guestCounts[dropdownData.seatId] || startNum}
|
|
195
|
+
onChange={event => {
|
|
196
|
+
setGuestCounts({
|
|
197
|
+
...guestCounts,
|
|
198
|
+
[dropdownData.seatId]: Number(
|
|
199
|
+
event.target.value
|
|
200
|
+
),
|
|
201
|
+
})
|
|
202
|
+
}}
|
|
203
|
+
inputProps={{ 'aria-label': 'Without label' }}
|
|
204
|
+
MenuProps={{
|
|
205
|
+
PaperProps: {
|
|
206
|
+
sx: { maxHeight: 150 },
|
|
207
|
+
className: 'get-tickets-paper',
|
|
208
|
+
},
|
|
209
|
+
}}
|
|
210
|
+
displayEmpty
|
|
211
|
+
sx={{ borderRadius: 0 }}
|
|
212
|
+
>
|
|
213
|
+
{_map(
|
|
214
|
+
Array.from(
|
|
215
|
+
{ length: numLength },
|
|
216
|
+
(_, i) => startNum + i
|
|
217
|
+
),
|
|
218
|
+
(option, index) => (
|
|
219
|
+
<MenuItem value={option} key={index}>
|
|
220
|
+
{option}
|
|
221
|
+
</MenuItem>
|
|
222
|
+
)
|
|
223
|
+
)}
|
|
224
|
+
</Select>
|
|
225
|
+
</FormControl>
|
|
226
|
+
</div>
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
)
|
|
232
|
+
})}
|
|
233
|
+
</div>
|
|
234
|
+
<div>
|
|
235
|
+
<Button
|
|
236
|
+
className={`book-button
|
|
237
|
+
${bookButtonIsDisabled ? 'disabled' : ''}
|
|
238
|
+
${isButtonScrollable ? 'is-scrollable' : ''}
|
|
239
|
+
`}
|
|
240
|
+
onClick={!bookButtonIsDisabled ? handleGetTicketClick : _identity}
|
|
241
|
+
disabled={isAddingToCart}
|
|
242
|
+
>
|
|
243
|
+
{isAddingToCart ? (
|
|
244
|
+
<CircularProgress size={20} style={{ marginLeft: 10 }} />
|
|
245
|
+
) : (
|
|
246
|
+
getTicketsBtnLabel ||
|
|
247
|
+
getButtonLabel(reservedSeats.length, tableMapEnabled)
|
|
248
|
+
)}
|
|
249
|
+
</Button>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</ThemeProvider>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import _get from 'lodash/get'
|
|
2
|
+
|
|
3
|
+
import { addToCart, getCheckoutPageConfigs, postOnCheckout } from '../../api'
|
|
4
|
+
import { createCheckoutDataBodyWithDefaultHolder } from '../../utils'
|
|
5
|
+
|
|
6
|
+
interface IAddToCartFuncProps {
|
|
7
|
+
eventId: string | number;
|
|
8
|
+
data: any;
|
|
9
|
+
ticketQuantity: number;
|
|
10
|
+
enableBillingInfoAutoCreate?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const addToCartFunc = async ({
|
|
14
|
+
eventId,
|
|
15
|
+
data,
|
|
16
|
+
ticketQuantity,
|
|
17
|
+
enableBillingInfoAutoCreate = true,
|
|
18
|
+
}: IAddToCartFuncProps) => {
|
|
19
|
+
const isWindowDefined = typeof window !== 'undefined'
|
|
20
|
+
|
|
21
|
+
const result = await addToCart(eventId, data)
|
|
22
|
+
const pageConfigsDataResponse = await getCheckoutPageConfigs()
|
|
23
|
+
|
|
24
|
+
if (result.status === 200 && pageConfigsDataResponse.status === 200) {
|
|
25
|
+
const pageConfigsData =
|
|
26
|
+
_get(pageConfigsDataResponse, 'data.attributes') || {}
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
skip_billing_page: skipBillingPage = false,
|
|
30
|
+
names_required: nameIsRequired = false,
|
|
31
|
+
age_required: ageIsRequired = false,
|
|
32
|
+
phone_required: phoneIsRequired = false,
|
|
33
|
+
hide_phone_field: hidePhoneField = false,
|
|
34
|
+
has_add_on: hasAddOn = false,
|
|
35
|
+
free_ticket: freeTicket = false,
|
|
36
|
+
collect_optional_wallet_address: collectOptionalWalletAddress = false,
|
|
37
|
+
collect_mandatory_wallet_address: collectMandatoryWalletAddress = false,
|
|
38
|
+
} = pageConfigsData
|
|
39
|
+
|
|
40
|
+
let hash = ''
|
|
41
|
+
let total = ''
|
|
42
|
+
|
|
43
|
+
isWindowDefined && window.localStorage.removeItem('add_ons')
|
|
44
|
+
|
|
45
|
+
if (skipBillingPage && !hasAddOn) {
|
|
46
|
+
// Get user data for checkout data
|
|
47
|
+
const userData =
|
|
48
|
+
isWindowDefined && window.localStorage.getItem('user_data')
|
|
49
|
+
? JSON.parse(window.localStorage.getItem('user_data') || '')
|
|
50
|
+
: {}
|
|
51
|
+
|
|
52
|
+
const checkoutBody = createCheckoutDataBodyWithDefaultHolder(
|
|
53
|
+
ticketQuantity,
|
|
54
|
+
userData
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
const checkoutResult = enableBillingInfoAutoCreate
|
|
58
|
+
? await postOnCheckout(checkoutBody, undefined, freeTicket)
|
|
59
|
+
: null
|
|
60
|
+
|
|
61
|
+
hash = _get(checkoutResult, 'data.data.attributes.hash') || ''
|
|
62
|
+
total = _get(checkoutResult, 'data.data.attributes.total') || ''
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
skip_billing_page: skipBillingPage,
|
|
67
|
+
names_required: nameIsRequired,
|
|
68
|
+
phone_required: phoneIsRequired,
|
|
69
|
+
age_required: ageIsRequired,
|
|
70
|
+
hide_phone_field: hidePhoneField,
|
|
71
|
+
free_ticket: freeTicket,
|
|
72
|
+
collect_optional_wallet_address: collectOptionalWalletAddress,
|
|
73
|
+
collect_mandatory_wallet_address: collectMandatoryWalletAddress,
|
|
74
|
+
event_id: String(eventId),
|
|
75
|
+
hash,
|
|
76
|
+
total,
|
|
77
|
+
hasAddOn,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null
|
|
82
|
+
}
|