tf-checkout-react 1.6.6-beta.17 → 1.6.6-beta.19
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/adapters/customFields.d.ts +1 -0
- package/dist/api/common.d.ts +1 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/components/addonsContainer/AddonComponent.d.ts +5 -1
- package/dist/components/addonsContainer/index.d.ts +3 -1
- package/dist/components/billing-info-container/index.d.ts +1 -0
- package/dist/components/ticketsContainer/TimeSlotsSection.d.ts +17 -0
- package/dist/tf-checkout-react.cjs.development.js +891 -638
- 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 +892 -639
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/checkoutPageConfigs.d.ts +1 -1
- package/dist/utils/customFields.d.ts +11 -0
- package/package.json +1 -1
- package/src/adapters/customFields.ts +7 -1
- package/src/api/common.ts +6 -0
- package/src/api/index.ts +1 -1
- package/src/components/addonsContainer/AddonComponent.tsx +65 -11
- package/src/components/addonsContainer/index.tsx +15 -2
- package/src/components/billing-info-container/index.tsx +79 -72
- package/src/components/common/CheckboxField/index.tsx +1 -1
- package/src/components/ticketsContainer/TicketRow.tsx +1 -1
- package/src/components/ticketsContainer/TicketsSection.tsx +2 -2
- package/src/components/ticketsContainer/TimeSlotsSection.tsx +118 -0
- package/src/components/ticketsContainer/index.tsx +49 -12
- package/src/hoc/CustomFields/index.tsx +8 -0
- package/src/types/api/common.d.ts +4 -0
- package/src/types/checkoutPageConfigs.ts +1 -1
- package/src/utils/customFields.ts +22 -0
|
@@ -2,7 +2,7 @@ export interface ICheckoutPageConfigs {
|
|
|
2
2
|
age_required: boolean;
|
|
3
3
|
event_id: string;
|
|
4
4
|
has_add_on: boolean;
|
|
5
|
-
minimum_age
|
|
5
|
+
minimum_age?: number | string | null;
|
|
6
6
|
names_required: boolean;
|
|
7
7
|
phone_required: boolean;
|
|
8
8
|
skip_billing_page: boolean;
|
|
@@ -21,4 +21,15 @@ export declare const getDataWithCustomFields: (initialData: IBillingInfoData[],
|
|
|
21
21
|
uniqueId?: string | undefined;
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
|
+
export declare const getAddOnDataWithCustomFields: (customFields: any) => {
|
|
25
|
+
addOnDataWithCustomFields: {};
|
|
26
|
+
} | {
|
|
27
|
+
addOnDataWithCustomFields: {
|
|
28
|
+
fields: {
|
|
29
|
+
id: number;
|
|
30
|
+
groupClassname: string;
|
|
31
|
+
groupItems: any[];
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
};
|
|
24
35
|
export declare const getFieldsKeys: (customFields: any) => any[];
|
package/package.json
CHANGED
|
@@ -61,10 +61,12 @@ export const fieldDataAdapter = (field: IGroupItem) => {
|
|
|
61
61
|
export const customFieldsDataAdapter = (data: any) => {
|
|
62
62
|
const adaptedTicketFields: Array<IGroupItem> = []
|
|
63
63
|
const adaptedOrderFields: Array<IGroupItem> = []
|
|
64
|
+
const adaptedAddOnFields: Array<IGroupItem> = []
|
|
64
65
|
|
|
65
66
|
_forEach(data, field => {
|
|
66
67
|
const ticketFields = _get(field, 'ticket.group.fields')
|
|
67
68
|
const orderFields = _get(field, 'order.group.fields')
|
|
69
|
+
const addOnFields = _get(field, 'add-on.group.fields')
|
|
68
70
|
|
|
69
71
|
_forEach(ticketFields, ticketField => {
|
|
70
72
|
adaptedTicketFields.push(fieldDataAdapter(ticketField))
|
|
@@ -73,7 +75,11 @@ export const customFieldsDataAdapter = (data: any) => {
|
|
|
73
75
|
_forEach(orderFields, orderField => {
|
|
74
76
|
adaptedOrderFields.push(fieldDataAdapter(orderField))
|
|
75
77
|
})
|
|
78
|
+
|
|
79
|
+
_forEach(addOnFields, addOnField => {
|
|
80
|
+
adaptedAddOnFields.push(fieldDataAdapter(addOnField))
|
|
81
|
+
})
|
|
76
82
|
})
|
|
77
83
|
|
|
78
|
-
return { ticketsFields: adaptedTicketFields, orderFields: adaptedOrderFields }
|
|
84
|
+
return { ticketsFields: adaptedTicketFields, orderFields: adaptedOrderFields, addOnFields: adaptedAddOnFields }
|
|
79
85
|
}
|
package/src/api/common.ts
CHANGED
|
@@ -89,6 +89,12 @@ export const getTickets = async (
|
|
|
89
89
|
return adaptedResponse.data
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
export const getTimeSlotsData = async (eventId: string | number): Promise<TimeSlotsResponse> => {
|
|
93
|
+
const response: AxiosResponse<TimeSlotsResponse, AxiosRequestConfig> =
|
|
94
|
+
await publicRequest.get(`/event/${eventId}/time-slot-groups-dates/`) // This endpoint currently works only for dashboard
|
|
95
|
+
return response.data
|
|
96
|
+
}
|
|
97
|
+
|
|
92
98
|
export const getCountries = async (): Promise<ICountriesResponse> => {
|
|
93
99
|
const response: AxiosResponse<ICountriesResponse, AxiosRequestConfig> =
|
|
94
100
|
await publicRequest.get('/countries/list')
|
package/src/api/index.ts
CHANGED
|
@@ -155,7 +155,7 @@ export const getCheckoutPageConfigs = async (): Promise<ResponseConfigs> => {
|
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
export const getCustomFields = async (eventId: string) => {
|
|
158
|
-
const response = await publicRequest.get(`/v1/event/${eventId}/
|
|
158
|
+
const response = await publicRequest.get(`/v1/event/${eventId}/custom_fields`)
|
|
159
159
|
const customFields = _get(response, 'data.data.attributes', [])
|
|
160
160
|
const adaptedResponse = customFieldsDataAdapter(customFields)
|
|
161
161
|
return adaptedResponse
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import { Field } from 'formik'
|
|
2
3
|
import _identity from 'lodash/identity'
|
|
4
|
+
import _isEmpty from 'lodash/isEmpty'
|
|
3
5
|
import _isNull from 'lodash/isNull'
|
|
6
|
+
import _map from 'lodash/map'
|
|
4
7
|
import React from 'react'
|
|
5
8
|
|
|
9
|
+
import { getFieldComponent, getFieldLabel, getValidateFunctions } from '../billing-info-container/utils'
|
|
6
10
|
import { NativeSelectField } from '../common'
|
|
7
11
|
|
|
12
|
+
// TO DO:
|
|
13
|
+
// - Need to add custom fields validations
|
|
14
|
+
// - Need to apply correct styles
|
|
15
|
+
|
|
8
16
|
interface IAddonComponentProps {
|
|
9
17
|
classNamePrefix: string;
|
|
10
18
|
data: any;
|
|
11
19
|
selectOptions: any;
|
|
12
20
|
handleAddonChange?: (...res: any) => void;
|
|
21
|
+
addOnDataWithCustomFields: any;
|
|
22
|
+
configs: any;
|
|
23
|
+
values: any;
|
|
24
|
+
errors: any;
|
|
13
25
|
}
|
|
14
26
|
|
|
15
27
|
const AddonComponent = ({
|
|
@@ -17,6 +29,10 @@ const AddonComponent = ({
|
|
|
17
29
|
data,
|
|
18
30
|
selectOptions,
|
|
19
31
|
handleAddonChange = _identity,
|
|
32
|
+
addOnDataWithCustomFields,
|
|
33
|
+
configs,
|
|
34
|
+
values,
|
|
35
|
+
errors,
|
|
20
36
|
}: IAddonComponentProps) => {
|
|
21
37
|
const { id, name, active, stock } = data
|
|
22
38
|
|
|
@@ -28,17 +44,55 @@ const AddonComponent = ({
|
|
|
28
44
|
{!active || (!_isNull(stock) && stock <= 0) ? (
|
|
29
45
|
<div className="sold_out">SOLD OUT</div>
|
|
30
46
|
) : (
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
<>
|
|
48
|
+
<div className={`${classNamePrefix}_product_qty_select`}>
|
|
49
|
+
<Field
|
|
50
|
+
name={id}
|
|
51
|
+
selectOptions={selectOptions}
|
|
52
|
+
component={NativeSelectField}
|
|
53
|
+
onChange={(e: any) => {
|
|
54
|
+
const { value } = e.target
|
|
55
|
+
handleAddonChange(id, value)
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
{!_isEmpty(addOnDataWithCustomFields?.fields) && (
|
|
60
|
+
<div className="ticket-holders-fields">
|
|
61
|
+
<h2>{addOnDataWithCustomFields.label}</h2>
|
|
62
|
+
{_map(addOnDataWithCustomFields.fields, group => {
|
|
63
|
+
const { id, groupClassname, groupItems } = group
|
|
64
|
+
return (
|
|
65
|
+
<div key={id}>
|
|
66
|
+
<div className={groupClassname}>
|
|
67
|
+
{_map(groupItems, element => (
|
|
68
|
+
<div className={element.className} key={element.name}>
|
|
69
|
+
<Field
|
|
70
|
+
{...element}
|
|
71
|
+
type={
|
|
72
|
+
element.type === 'radio' ||
|
|
73
|
+
element.type === 'checkbox'
|
|
74
|
+
? undefined
|
|
75
|
+
: element.type
|
|
76
|
+
}
|
|
77
|
+
name={`${element.name}`}
|
|
78
|
+
label={getFieldLabel(element, configs)}
|
|
79
|
+
component={getFieldComponent(element)}
|
|
80
|
+
validate={getValidateFunctions(
|
|
81
|
+
element,
|
|
82
|
+
[],
|
|
83
|
+
values,
|
|
84
|
+
errors
|
|
85
|
+
)}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
))}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
})}
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
</>
|
|
42
96
|
)}
|
|
43
97
|
</div>
|
|
44
98
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
/* eslint-disable react/no-unescaped-entities */
|
|
2
3
|
|
|
3
4
|
import { CircularProgress } from '@mui/material'
|
|
@@ -46,6 +47,8 @@ export interface IAddonContainterProps {
|
|
|
46
47
|
onPendingVerification?: () => void;
|
|
47
48
|
samePage?: boolean;
|
|
48
49
|
descriptionTrigger?: "click" | "hover" | "always";
|
|
50
|
+
addOnDataWithCustomFields: any;
|
|
51
|
+
configs: any;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
export interface ObjectLiteral {
|
|
@@ -66,6 +69,8 @@ export const AddonsContainter = ({
|
|
|
66
69
|
onPendingVerification = _identity,
|
|
67
70
|
samePage,
|
|
68
71
|
descriptionTrigger = 'click',
|
|
72
|
+
addOnDataWithCustomFields,
|
|
73
|
+
configs,
|
|
69
74
|
}: IAddonContainterProps) => {
|
|
70
75
|
const eventId = getQueryVariable('event_id')
|
|
71
76
|
const [addons, setAddons] = useState<any>([])
|
|
@@ -331,7 +336,7 @@ export const AddonsContainter = ({
|
|
|
331
336
|
}
|
|
332
337
|
} : undefined}
|
|
333
338
|
>
|
|
334
|
-
{({ values }) => {
|
|
339
|
+
{({ values, errors }) => {
|
|
335
340
|
const isConfirmDisabled = !isAtLeastOneAddonSelected(values)
|
|
336
341
|
|
|
337
342
|
return (
|
|
@@ -417,7 +422,7 @@ export const AddonsContainter = ({
|
|
|
417
422
|
)}
|
|
418
423
|
</div>
|
|
419
424
|
</div>
|
|
420
|
-
{(visibleDescription === addon.id || descriptionTrigger === 'always'
|
|
425
|
+
{(visibleDescription === addon.id || descriptionTrigger === 'always') && (
|
|
421
426
|
<div
|
|
422
427
|
className={`${classNamePrefix}_product_desc`}
|
|
423
428
|
dangerouslySetInnerHTML={createMarkup(addon.description)}
|
|
@@ -435,6 +440,10 @@ export const AddonsContainter = ({
|
|
|
435
440
|
handleAddonChange={(id, value) =>
|
|
436
441
|
onFieldChange(id, value, addon)
|
|
437
442
|
}
|
|
443
|
+
addOnDataWithCustomFields={addOnDataWithCustomFields}
|
|
444
|
+
configs={configs}
|
|
445
|
+
values={values}
|
|
446
|
+
errors={errors}
|
|
438
447
|
/>
|
|
439
448
|
))
|
|
440
449
|
) : (
|
|
@@ -447,6 +456,10 @@ export const AddonsContainter = ({
|
|
|
447
456
|
handleAddonChange={(id, value) =>
|
|
448
457
|
onFieldChange(id, value, addon)
|
|
449
458
|
}
|
|
459
|
+
addOnDataWithCustomFields={addOnDataWithCustomFields}
|
|
460
|
+
configs={configs}
|
|
461
|
+
values={values}
|
|
462
|
+
errors={errors}
|
|
450
463
|
/>
|
|
451
464
|
)}
|
|
452
465
|
</div>
|
|
@@ -113,6 +113,7 @@ export interface IBillingInfoPage {
|
|
|
113
113
|
onPendingVerification?: () => void;
|
|
114
114
|
includeAddons?: boolean;
|
|
115
115
|
addonsProps?: IAddonContainterProps;
|
|
116
|
+
addOnDataWithCustomFields?: any;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
const LogicRunner: FC<{
|
|
@@ -136,72 +137,72 @@ const LogicRunner: FC<{
|
|
|
136
137
|
shouldFetchCountries,
|
|
137
138
|
brandOptIn,
|
|
138
139
|
}) => {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
140
|
+
const prevCountry = useRef(values.country)
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
const fetchStates = async () => {
|
|
143
|
+
try {
|
|
144
|
+
const res = await getStates(values.country)
|
|
145
|
+
const mappedStates = _map(res.data, (item, key) => ({
|
|
146
|
+
label: item,
|
|
147
|
+
value: key,
|
|
148
|
+
}))
|
|
149
|
+
setStates(mappedStates)
|
|
150
|
+
if (prevCountry.current !== values.country) {
|
|
151
|
+
const stateExists = mappedStates.find(
|
|
152
|
+
state => state.value === values.state
|
|
153
|
+
)?.value
|
|
154
|
+
setFieldValue('state', stateExists ?? mappedStates[0]?.value ?? '')
|
|
155
|
+
prevCountry.current = values.country
|
|
156
|
+
}
|
|
157
|
+
onGetStatesSuccess(res.data)
|
|
158
|
+
} catch (e) {
|
|
159
|
+
if (axios.isAxiosError(e)) {
|
|
160
|
+
onGetStatesError(e)
|
|
161
|
+
}
|
|
160
162
|
}
|
|
161
163
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
164
|
+
shouldFetchCountries && fetchStates()
|
|
165
|
+
}, [values.country, setStates, setFieldValue])
|
|
166
|
+
const userDataEncoded = isBrowser ? window.localStorage.getItem('user_data') : ''
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
// set user data from local storage
|
|
169
|
+
const getStoredUserData = () => {
|
|
170
|
+
if (isBrowser) {
|
|
171
|
+
if (userDataEncoded) {
|
|
172
|
+
try {
|
|
173
|
+
const parsedData = JSON.parse(userDataEncoded)
|
|
174
|
+
const mappedValues = {
|
|
175
|
+
firstName: parsedData?.first_name || parsedData?.firstName || '',
|
|
176
|
+
lastName: parsedData?.last_name || parsedData?.lastName || '',
|
|
177
|
+
email: parsedData?.email || '',
|
|
178
|
+
phone: parsedData?.phone || '',
|
|
179
|
+
confirmEmail: parsedData?.email || '',
|
|
180
|
+
state: parsedData?.state || '',
|
|
181
|
+
street_address: parsedData?.street_address || '',
|
|
182
|
+
country: parsedData?.country || '1',
|
|
183
|
+
zip: parsedData?.zip || '',
|
|
184
|
+
brand_opt_in: brandOptIn ? brandOptIn : parsedData?.brand_opt_in || false,
|
|
185
|
+
city: parsedData?.city || '',
|
|
186
|
+
confirmPassword: '',
|
|
187
|
+
password: '',
|
|
188
|
+
'holderFirstName-0': parsedData?.first_name || parsedData?.firstName || '',
|
|
189
|
+
'holderLastName-0': parsedData?.last_name || parsedData?.lastName || '',
|
|
190
|
+
'holderEmail-0': parsedData?.email || '',
|
|
191
|
+
}
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
const extraDataJSON = window.localStorage.getItem('extraData')
|
|
194
|
+
const extraData = extraDataJSON ? JSON.parse(extraDataJSON) : null
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
setValues({ ...values, ...mappedValues, ...(extraData ?? {}) })
|
|
197
|
+
setUserValues(mappedValues)
|
|
198
|
+
} catch (e) { }
|
|
199
|
+
}
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
202
|
+
getStoredUserData()
|
|
203
|
+
}, [userDataEncoded, setValues, setUserValues])
|
|
204
|
+
return null
|
|
205
|
+
}
|
|
205
206
|
|
|
206
207
|
const BillingInfoContainer = React.memo(
|
|
207
208
|
({
|
|
@@ -254,6 +255,7 @@ const BillingInfoContainer = React.memo(
|
|
|
254
255
|
onGetCheckoutConfigsError = _identity,
|
|
255
256
|
includeAddons = false,
|
|
256
257
|
addonsProps,
|
|
258
|
+
addOnDataWithCustomFields,
|
|
257
259
|
}: IBillingInfoPage) => {
|
|
258
260
|
const [extraData, setExtraData] = useState(null)
|
|
259
261
|
const [isConfigLoading, setIsConfigLoading] = useState(true)
|
|
@@ -625,7 +627,7 @@ const BillingInfoContainer = React.memo(
|
|
|
625
627
|
{
|
|
626
628
|
country: initialCountry,
|
|
627
629
|
state: _get(userData, 'stateId', '') || '1',
|
|
628
|
-
brand_opt_in: optedInFieldValue,
|
|
630
|
+
brand_opt_in: Boolean(optedInFieldValue),
|
|
629
631
|
ttf_opt_in: ttfOptIn,
|
|
630
632
|
data_capture: {
|
|
631
633
|
instagram: _get(extraData, 'data_capture.instagram', ''),
|
|
@@ -867,7 +869,13 @@ const BillingInfoContainer = React.memo(
|
|
|
867
869
|
</div>
|
|
868
870
|
</div>
|
|
869
871
|
)}
|
|
870
|
-
{includeAddons ?
|
|
872
|
+
{includeAddons ? (
|
|
873
|
+
<AddonsContainter
|
|
874
|
+
{...(addonsProps ?? {})}
|
|
875
|
+
addOnDataWithCustomFields={addOnDataWithCustomFields}
|
|
876
|
+
configs={configs}
|
|
877
|
+
/>
|
|
878
|
+
) : null}
|
|
871
879
|
{!cardLoading &&
|
|
872
880
|
_map(dataWithUniqueIds, item => {
|
|
873
881
|
const { label, labelClassName, fields } = item
|
|
@@ -974,9 +982,8 @@ const BillingInfoContainer = React.memo(
|
|
|
974
982
|
].includes(element.name) && isLoggedIn ? null : (
|
|
975
983
|
<React.Fragment key={element.uniqueId}>
|
|
976
984
|
<div
|
|
977
|
-
className={`${element.className} ${
|
|
978
|
-
|
|
979
|
-
}`}
|
|
985
|
+
className={`${element.className} ${props?.errors[element.name] || ''
|
|
986
|
+
}`}
|
|
980
987
|
>
|
|
981
988
|
{element.component ? (
|
|
982
989
|
element.component
|
|
@@ -985,7 +992,7 @@ const BillingInfoContainer = React.memo(
|
|
|
985
992
|
{...element}
|
|
986
993
|
type={
|
|
987
994
|
element.type === 'radio' ||
|
|
988
|
-
|
|
995
|
+
element.type === 'checkbox'
|
|
989
996
|
? undefined
|
|
990
997
|
: element.type
|
|
991
998
|
}
|
|
@@ -1007,11 +1014,11 @@ const BillingInfoContainer = React.memo(
|
|
|
1007
1014
|
selectOptions={
|
|
1008
1015
|
element.name === 'country'
|
|
1009
1016
|
? _map(countries, item => ({
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1017
|
+
value: item.id,
|
|
1018
|
+
label: item.name,
|
|
1019
|
+
}))
|
|
1013
1020
|
: element.name === 'state'
|
|
1014
|
-
|
|
1021
|
+
? [
|
|
1015
1022
|
{
|
|
1016
1023
|
label: element.label,
|
|
1017
1024
|
value: '',
|
|
@@ -1019,7 +1026,7 @@ const BillingInfoContainer = React.memo(
|
|
|
1019
1026
|
},
|
|
1020
1027
|
...states,
|
|
1021
1028
|
]
|
|
1022
|
-
|
|
1029
|
+
: element.selectOptions || []
|
|
1023
1030
|
}
|
|
1024
1031
|
theme={theme}
|
|
1025
1032
|
defaultCountry={
|
|
@@ -1059,7 +1066,7 @@ const BillingInfoContainer = React.memo(
|
|
|
1059
1066
|
{...element}
|
|
1060
1067
|
type={
|
|
1061
1068
|
element.type === 'radio' ||
|
|
1062
|
-
|
|
1069
|
+
element.type === 'checkbox'
|
|
1063
1070
|
? undefined
|
|
1064
1071
|
: element.type
|
|
1065
1072
|
}
|
|
@@ -30,7 +30,7 @@ export const CheckboxField = ({
|
|
|
30
30
|
>
|
|
31
31
|
<FormGroup>
|
|
32
32
|
<FormControlLabel
|
|
33
|
-
control={<Checkbox {...field} checked={field.value} />}
|
|
33
|
+
control={<Checkbox {...field} checked={Boolean(field.value)} />}
|
|
34
34
|
label={label}
|
|
35
35
|
componentsProps={{ typography: customTheme?.checkbox }}
|
|
36
36
|
/>
|
|
@@ -83,7 +83,7 @@ export const TicketRow = ({
|
|
|
83
83
|
// ticketTier.soldOut === false --> means that ticket is in the stock
|
|
84
84
|
const isSoldOut =
|
|
85
85
|
ticketTier.sold_out ||
|
|
86
|
-
!ticketTier.displayTicket ||
|
|
86
|
+
!(ticketTier.displayTicket || ticketTier.slotGroupId) ||
|
|
87
87
|
ticketTier.soldOut ||
|
|
88
88
|
ticketTier.soldOut === false
|
|
89
89
|
|
|
@@ -70,7 +70,7 @@ export const TicketsSection = ({
|
|
|
70
70
|
const ticketPriceWithFees = `${priceSymbol} ${(+ticket.price).toFixed(2)}`
|
|
71
71
|
const ticketOldPrice = `${priceSymbol} ${(+ticket.oldPrice).toFixed(2)}`
|
|
72
72
|
|
|
73
|
-
const isSoldOut = ticket.sold_out || !ticket.displayTicket || ticket.soldOut
|
|
73
|
+
const isSoldOut = ticket.sold_out || !(ticket.displayTicket || ticket.slotGroupId) || ticket.soldOut
|
|
74
74
|
const ticketSelect = (event: any) => {
|
|
75
75
|
const { value } = event.target
|
|
76
76
|
handleTicketSelect(ticket.id, value)
|
|
@@ -168,7 +168,7 @@ export const TicketsSection = ({
|
|
|
168
168
|
const ticketPriceWithFees = `${priceSymbol} ${(+ticket.price).toFixed(2)}`
|
|
169
169
|
const ticketOldPrice = `${priceSymbol} ${(+ticket.oldPrice).toFixed(2)}`
|
|
170
170
|
|
|
171
|
-
const isSoldOut = ticket.sold_out || !ticket.displayTicket || ticket.soldOut
|
|
171
|
+
const isSoldOut = ticket.sold_out || !(ticket.displayTicket || ticket.slotGroupId) || ticket.soldOut
|
|
172
172
|
const ticketSelect = (event: any) => {
|
|
173
173
|
const { value } = event.target
|
|
174
174
|
handleTicketSelect(ticket.id, value, true)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Box, CircularProgress, TextField } from "@mui/material"
|
|
3
|
+
import { LocalizationProvider, StaticDatePicker as DatePicker } from "@mui/x-date-pickers"
|
|
4
|
+
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment"
|
|
5
|
+
import axios from "axios"
|
|
6
|
+
import _isEmpty from 'lodash/isEmpty'
|
|
7
|
+
import moment from "moment-timezone"
|
|
8
|
+
import React, { Dispatch, ReactNode, SetStateAction, useState } from "react"
|
|
9
|
+
|
|
10
|
+
import { TicketsSection } from "./TicketsSection"
|
|
11
|
+
|
|
12
|
+
interface Ticket {
|
|
13
|
+
id: string | number;
|
|
14
|
+
type: string;
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
event: any;
|
|
20
|
+
eventId: number;
|
|
21
|
+
availableDates: string[];
|
|
22
|
+
|
|
23
|
+
selectedTickets: any;
|
|
24
|
+
setSelectedTickets: Dispatch<SetStateAction<any>>;
|
|
25
|
+
handleTicketSelect: any;
|
|
26
|
+
sortBySoldOut: boolean;
|
|
27
|
+
hideTicketsHeader: boolean;
|
|
28
|
+
ticketsHeaderComponent?: ReactNode;
|
|
29
|
+
showGroupNameBlock?: boolean;
|
|
30
|
+
currencySybmol?: string;
|
|
31
|
+
isSeatMapAllowed?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const TimeSlotsSection: React.FC<Props> = ({
|
|
35
|
+
event,
|
|
36
|
+
eventId,
|
|
37
|
+
availableDates,
|
|
38
|
+
selectedTickets,
|
|
39
|
+
setSelectedTickets,
|
|
40
|
+
handleTicketSelect,
|
|
41
|
+
sortBySoldOut,
|
|
42
|
+
hideTicketsHeader,
|
|
43
|
+
ticketsHeaderComponent,
|
|
44
|
+
showGroupNameBlock,
|
|
45
|
+
currencySybmol,
|
|
46
|
+
isSeatMapAllowed }) => {
|
|
47
|
+
const [selectedDate, setSelectedDate] = useState<string | null>(null)
|
|
48
|
+
const [tickets, setTickets] = useState<Ticket[]>([])
|
|
49
|
+
const [loading, setLoading] = useState(false)
|
|
50
|
+
|
|
51
|
+
const handleDateChange = async (date: string | null) => {
|
|
52
|
+
setSelectedDate(date)
|
|
53
|
+
if (date) {
|
|
54
|
+
setLoading(true)
|
|
55
|
+
try {
|
|
56
|
+
// Note this is temp endpoint
|
|
57
|
+
const response = await axios.get(`/api/time-slots`, {
|
|
58
|
+
params: {
|
|
59
|
+
eventId,
|
|
60
|
+
date: moment(date).format("YYYY-MM-DD"),
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
setTickets(response.data.tickets)
|
|
64
|
+
setSelectedTickets(response.data.tickets)
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error("Error fetching time slots:", error)
|
|
67
|
+
setTickets([])
|
|
68
|
+
setSelectedTickets([])
|
|
69
|
+
} finally {
|
|
70
|
+
setLoading(false)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const isDateDisabled = (date: string) => {
|
|
76
|
+
const formattedDate = moment(date).format("YYYY-MM-DD")
|
|
77
|
+
return !availableDates.includes(formattedDate)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
|
|
82
|
+
<LocalizationProvider dateAdapter={AdapterMoment}>
|
|
83
|
+
<DatePicker
|
|
84
|
+
value={selectedDate}
|
|
85
|
+
onChange={handleDateChange}
|
|
86
|
+
shouldDisableDate={isDateDisabled}
|
|
87
|
+
renderInput={params => <TextField {...params} />}
|
|
88
|
+
showToolbar={false}
|
|
89
|
+
componentsProps={{ actionBar: { actions: [] } }}
|
|
90
|
+
disablePast
|
|
91
|
+
/>
|
|
92
|
+
</LocalizationProvider>
|
|
93
|
+
{loading ? (
|
|
94
|
+
<CircularProgress sx={{ marginTop: 2 }} />
|
|
95
|
+
) : (
|
|
96
|
+
<Box sx={{ marginTop: 2, width: "100%" }}>
|
|
97
|
+
<TicketsSection
|
|
98
|
+
event={event}
|
|
99
|
+
ticketsList={tickets}
|
|
100
|
+
tableTickets={[]}
|
|
101
|
+
selectedTickets={selectedTickets}
|
|
102
|
+
handleTicketSelect={handleTicketSelect}
|
|
103
|
+
sortBySoldOut={sortBySoldOut}
|
|
104
|
+
ticketsHeaderComponent={ticketsHeaderComponent}
|
|
105
|
+
tableTicketsHeaderComponent={null}
|
|
106
|
+
hideTableTicketsHeader={true}
|
|
107
|
+
hideTicketsHeader={hideTicketsHeader || _isEmpty(tickets)}
|
|
108
|
+
showGroupNameBlock={showGroupNameBlock}
|
|
109
|
+
currencySybmol={currencySybmol}
|
|
110
|
+
isSeatMapAllowed={isSeatMapAllowed}
|
|
111
|
+
/>
|
|
112
|
+
</Box>
|
|
113
|
+
)}
|
|
114
|
+
</Box>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default TimeSlotsSection
|