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.
Files changed (31) hide show
  1. package/dist/adapters/customFields.d.ts +1 -0
  2. package/dist/api/common.d.ts +1 -0
  3. package/dist/api/index.d.ts +1 -0
  4. package/dist/components/addonsContainer/AddonComponent.d.ts +5 -1
  5. package/dist/components/addonsContainer/index.d.ts +3 -1
  6. package/dist/components/billing-info-container/index.d.ts +1 -0
  7. package/dist/components/ticketsContainer/TimeSlotsSection.d.ts +17 -0
  8. package/dist/tf-checkout-react.cjs.development.js +891 -638
  9. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  10. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  11. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  12. package/dist/tf-checkout-react.esm.js +892 -639
  13. package/dist/tf-checkout-react.esm.js.map +1 -1
  14. package/dist/types/checkoutPageConfigs.d.ts +1 -1
  15. package/dist/utils/customFields.d.ts +11 -0
  16. package/package.json +1 -1
  17. package/src/adapters/customFields.ts +7 -1
  18. package/src/api/common.ts +6 -0
  19. package/src/api/index.ts +1 -1
  20. package/src/components/addonsContainer/AddonComponent.tsx +65 -11
  21. package/src/components/addonsContainer/index.tsx +15 -2
  22. package/src/components/billing-info-container/index.tsx +79 -72
  23. package/src/components/common/CheckboxField/index.tsx +1 -1
  24. package/src/components/ticketsContainer/TicketRow.tsx +1 -1
  25. package/src/components/ticketsContainer/TicketsSection.tsx +2 -2
  26. package/src/components/ticketsContainer/TimeSlotsSection.tsx +118 -0
  27. package/src/components/ticketsContainer/index.tsx +49 -12
  28. package/src/hoc/CustomFields/index.tsx +8 -0
  29. package/src/types/api/common.d.ts +4 -0
  30. package/src/types/checkoutPageConfigs.ts +1 -1
  31. 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: number | string | null;
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
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.6.6-beta.17",
2
+ "version": "1.6.6-beta.19",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -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}/fields`)
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
- <div className={`${classNamePrefix}_product_qty_select`}>
32
- <Field
33
- name={id}
34
- selectOptions={selectOptions}
35
- component={NativeSelectField}
36
- onChange={(e: any) => {
37
- const { value } = e.target
38
- handleAddonChange(id, value)
39
- }}
40
- />
41
- </div>
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
- const prevCountry = useRef(values.country)
140
- useEffect(() => {
141
- const fetchStates = async () => {
142
- try {
143
- const res = await getStates(values.country)
144
- const mappedStates = _map(res.data, (item, key) => ({
145
- label: item,
146
- value: key,
147
- }))
148
- setStates(mappedStates)
149
- if (prevCountry.current !== values.country) {
150
- const stateExists = mappedStates.find(
151
- state => state.value === values.state
152
- )?.value
153
- setFieldValue('state', stateExists ?? mappedStates[0]?.value ?? '')
154
- prevCountry.current = values.country
155
- }
156
- onGetStatesSuccess(res.data)
157
- } catch (e) {
158
- if (axios.isAxiosError(e)) {
159
- onGetStatesError(e)
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
- shouldFetchCountries && fetchStates()
164
- }, [values.country, setStates, setFieldValue])
165
- const userDataEncoded = isBrowser ? window.localStorage.getItem('user_data') : ''
166
- useEffect(() => {
167
- // set user data from local storage
168
- const getStoredUserData = () => {
169
- if (isBrowser) {
170
- if (userDataEncoded) {
171
- try {
172
- const parsedData = JSON.parse(userDataEncoded)
173
- const mappedValues = {
174
- firstName: parsedData?.first_name || parsedData?.firstName || '',
175
- lastName: parsedData?.last_name || parsedData?.lastName || '',
176
- email: parsedData?.email || '',
177
- phone: parsedData?.phone || '',
178
- confirmEmail: parsedData?.email || '',
179
- state: parsedData?.state || '',
180
- street_address: parsedData?.street_address || '',
181
- country: parsedData?.country || '1',
182
- zip: parsedData?.zip || '',
183
- brand_opt_in: brandOptIn ? brandOptIn : parsedData?.brand_opt_in || false,
184
- city: parsedData?.city || '',
185
- confirmPassword: '',
186
- password: '',
187
- 'holderFirstName-0': parsedData?.first_name || parsedData?.firstName || '',
188
- 'holderLastName-0': parsedData?.last_name || parsedData?.lastName || '',
189
- 'holderEmail-0': parsedData?.email || '',
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
- const extraDataJSON = window.localStorage.getItem('extraData')
193
- const extraData = extraDataJSON ? JSON.parse(extraDataJSON) : null
193
+ const extraDataJSON = window.localStorage.getItem('extraData')
194
+ const extraData = extraDataJSON ? JSON.parse(extraDataJSON) : null
194
195
 
195
- setValues({ ...values, ...mappedValues, ...(extraData ?? {}) })
196
- setUserValues(mappedValues)
197
- } catch (e) {}
196
+ setValues({ ...values, ...mappedValues, ...(extraData ?? {}) })
197
+ setUserValues(mappedValues)
198
+ } catch (e) { }
199
+ }
198
200
  }
199
201
  }
200
- }
201
- getStoredUserData()
202
- }, [userDataEncoded, setValues, setUserValues])
203
- return null
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 ? <AddonsContainter {...(addonsProps ?? {})} /> : null}
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
- props?.errors[element.name] || ''
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
- element.type === 'checkbox'
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
- value: item.id,
1011
- label: item.name,
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
- : element.selectOptions || []
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
- element.type === 'checkbox'
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