tf-checkout-react 1.5.9 → 1.6.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.
Files changed (56) hide show
  1. package/dist/adapters/customFields.d.ts +11 -0
  2. package/dist/adapters/index.d.ts +1 -0
  3. package/dist/api/index.d.ts +6 -0
  4. package/dist/components/billing-info-container/index.d.ts +5 -2
  5. package/dist/components/billing-info-container/utils.d.ts +7 -5
  6. package/dist/components/common/Checkbox.d.ts +11 -0
  7. package/dist/components/common/CheckboxField/index.d.ts +9 -0
  8. package/dist/components/common/index.d.ts +1 -0
  9. package/dist/components/orderDetailsContainer/CustomFieldsForm.d.ts +12 -0
  10. package/dist/components/orderDetailsContainer/TicketHolderCustomFields.d.ts +10 -0
  11. package/dist/components/orderDetailsContainer/index.d.ts +29 -1
  12. package/dist/components/orderDetailsContainer/ticketsTable.d.ts +4 -1
  13. package/dist/components/orderDetailsContainer/utils/index.d.ts +6 -0
  14. package/dist/hoc/CustomFields/index.d.ts +3 -0
  15. package/dist/hoc/index.d.ts +1 -0
  16. package/dist/images/edit.svg +10 -0
  17. package/dist/tf-checkout-react.cjs.development.js +922 -210
  18. package/dist/tf-checkout-react.cjs.development.js.map +1 -1
  19. package/dist/tf-checkout-react.cjs.production.min.js +1 -1
  20. package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
  21. package/dist/tf-checkout-react.esm.js +924 -212
  22. package/dist/tf-checkout-react.esm.js.map +1 -1
  23. package/dist/types/billing-info-data.d.ts +2 -2
  24. package/dist/utils/createCheckoutDataBodyWithDefaultHolder.d.ts +3 -0
  25. package/dist/utils/customFields.d.ts +24 -0
  26. package/dist/utils/index.d.ts +1 -0
  27. package/package.json +1 -1
  28. package/src/adapters/customFields.ts +79 -0
  29. package/src/adapters/index.ts +1 -0
  30. package/src/api/index.ts +43 -0
  31. package/src/assets/images/edit.svg +10 -0
  32. package/src/components/billing-info-container/index.tsx +106 -60
  33. package/src/components/billing-info-container/utils.ts +29 -5
  34. package/src/components/common/{CheckboxField.tsx → Checkbox.tsx} +3 -3
  35. package/src/components/common/CheckboxField/index.tsx +41 -0
  36. package/src/components/common/CustomField.tsx +1 -1
  37. package/src/components/common/RadioGroupField/index.tsx +1 -1
  38. package/src/components/common/SelectField/index.tsx +1 -1
  39. package/src/components/common/index.tsx +1 -0
  40. package/src/components/orderDetailsContainer/CustomFieldsForm.tsx +75 -0
  41. package/src/components/orderDetailsContainer/TicketHolderCustomFields.tsx +100 -0
  42. package/src/components/orderDetailsContainer/index.tsx +175 -20
  43. package/src/components/orderDetailsContainer/ticketsTable.tsx +55 -33
  44. package/src/components/orderDetailsContainer/utils/index.tsx +55 -0
  45. package/src/components/paymentContainer/PaymentPlanSection.tsx +52 -35
  46. package/src/components/paymentContainer/index.tsx +2 -2
  47. package/src/components/stripePayment/index.tsx +2 -2
  48. package/src/components/ticketResale/index.tsx +4 -1
  49. package/src/components/ticketsContainer/index.tsx +1 -0
  50. package/src/hoc/CustomFields/index.tsx +77 -0
  51. package/src/hoc/index.ts +1 -0
  52. package/src/types/billing-info-data.ts +2 -2
  53. package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +3 -0
  54. package/src/utils/customFields.ts +58 -0
  55. package/src/utils/index.ts +1 -0
  56. package/dist/components/common/CheckboxField.d.ts +0 -11
@@ -1,4 +1,5 @@
1
1
  export { CheckboxField } from './CheckboxField'
2
+ export { Checkbox } from './Checkbox'
2
3
  export { CustomField } from './CustomField'
3
4
  export { FormikPhoneNumberField } from './FormikPhoneNumberField'
4
5
  export { PhoneNumberField } from './PhoneNumberField'
@@ -0,0 +1,75 @@
1
+ import { Field, Form, Formik, FormikProps } from 'formik'
2
+ import _identity from 'lodash/identity'
3
+ import React from 'react'
4
+
5
+ import { fieldDataAdapter } from '../../adapters/customFields'
6
+ import {
7
+ getFieldComponent,
8
+ getFieldLabel,
9
+ getValidateFunctions,
10
+ } from '../billing-info-container/utils'
11
+ import { CustomFieldTypes } from './index'
12
+
13
+ interface HolderCustomFieldsProps {
14
+ initialValues: { [key: string]: any };
15
+ fields: Array<CustomFieldTypes>;
16
+ handleFormSubmit?: (values: Record<string, any>) => void;
17
+ handleFormClose?: () => void;
18
+ }
19
+
20
+ const CustomFieldsForm = ({
21
+ initialValues = {},
22
+ fields = [],
23
+ handleFormSubmit = _identity,
24
+ handleFormClose = _identity,
25
+ }: HolderCustomFieldsProps) => (
26
+ <Formik
27
+ initialValues={initialValues}
28
+ onSubmit={values => handleFormSubmit(values)}
29
+ enableReinitialize={true}
30
+ >
31
+ {(props: FormikProps<any>) => (
32
+ <Form>
33
+ <div className="updatable-custom-fields">
34
+ {fields.map((customField: CustomFieldTypes) => {
35
+ const adaptedCustomField = fieldDataAdapter(customField)
36
+ return (
37
+ <Field
38
+ key={customField.id}
39
+ name={customField.name}
40
+ label={getFieldLabel(customField)}
41
+ component={getFieldComponent(adaptedCustomField)}
42
+ validate={getValidateFunctions(
43
+ customField,
44
+ [],
45
+ props.values,
46
+ props.errors
47
+ )}
48
+ isMultiple={adaptedCustomField.isMultiple}
49
+ radios={adaptedCustomField.radios}
50
+ options={adaptedCustomField.options}
51
+ />
52
+ )
53
+ })}
54
+ </div>
55
+ <div className="buttons-block">
56
+ <button
57
+ className="cancel-btn"
58
+ type="button"
59
+ onClick={() => {
60
+ props.resetForm()
61
+ handleFormClose()
62
+ }}
63
+ >
64
+ Cancel
65
+ </button>
66
+ <button className="submit-btn" type="submit">
67
+ Update ticket
68
+ </button>
69
+ </div>
70
+ </Form>
71
+ )}
72
+ </Formik>
73
+ )
74
+
75
+ export { CustomFieldsForm }
@@ -0,0 +1,100 @@
1
+ import _identity from 'lodash/identity'
2
+ import _isEmpty from 'lodash/isEmpty'
3
+ import React, { useMemo, useState } from 'react'
4
+ import SVG from 'react-inlinesvg'
5
+
6
+ import EditSvg from '../../assets/images/edit.svg'
7
+ import { CustomFieldsForm } from './CustomFieldsForm'
8
+ import { CustomFieldTypes } from './index'
9
+ import { ITicketTypes } from './ticketsTable'
10
+ import {
11
+ createCustomFieldsUpdateBody,
12
+ isFieldUpdatable,
13
+ renderCustomFieldValue,
14
+ } from './utils'
15
+
16
+ interface HolderCustomFieldsProps {
17
+ ticket: ITicketTypes;
18
+ holderCustomFields: CustomFieldTypes[];
19
+ handleTicketHoldersUpdate: (values: Record<string, any>, ticketHash: string) => void;
20
+ }
21
+
22
+ const TicketHolderCustomFields = ({
23
+ ticket,
24
+ holderCustomFields,
25
+ handleTicketHoldersUpdate = _identity,
26
+ }: HolderCustomFieldsProps) => {
27
+ const [showHolderCustomFieldsSection, setShowHolderCustomFieldsSection] =
28
+ useState(false)
29
+
30
+ const [allowedHolderCustomFields, notAllowedHolderCustomFields] = useMemo(() => {
31
+ const allowedFields: CustomFieldTypes[] = []
32
+ const notAllowedFields: CustomFieldTypes[] = []
33
+
34
+ holderCustomFields.forEach((field: CustomFieldTypes) => {
35
+ if (isFieldUpdatable(field)) {
36
+ allowedFields.push(field)
37
+ } else {
38
+ notAllowedFields.push(field)
39
+ }
40
+ })
41
+ return [allowedFields, notAllowedFields]
42
+ }, [holderCustomFields])
43
+
44
+ const holderEditableFieldsFormInitialValues = useMemo(
45
+ () => getHolderCustomFieldsInitialValues(holderCustomFields),
46
+ [holderCustomFields]
47
+ )
48
+
49
+ const handleFormClose = () => {
50
+ setShowHolderCustomFieldsSection(false)
51
+ }
52
+
53
+ const handleForSubmit = (values: Record<string, any>) => {
54
+ // Collect all updatable and non updatable values for bulk update
55
+ const fullUpdatableValues = createCustomFieldsUpdateBody(
56
+ values,
57
+ notAllowedHolderCustomFields
58
+ )
59
+ setShowHolderCustomFieldsSection(false)
60
+ handleTicketHoldersUpdate(fullUpdatableValues, ticket.hash)
61
+ }
62
+
63
+ function getHolderCustomFieldsInitialValues(fields: Array<CustomFieldTypes>) {
64
+ const initialValues: { [key: string]: string | boolean | string[] } = {}
65
+
66
+ for (const field of fields) {
67
+ if (isFieldUpdatable(field)) {
68
+ initialValues[field.name] =
69
+ field.type === 'checkbox' ? Boolean(field.value) : field.value || ''
70
+ }
71
+ }
72
+ return initialValues
73
+ }
74
+
75
+ return (
76
+ <>
77
+ <div className="holder-custom-fields">
78
+ {!_isEmpty(allowedHolderCustomFields) && <SVG src={EditSvg} onClick={() => setShowHolderCustomFieldsSection(true)} />}
79
+ {holderCustomFields.map((customField: CustomFieldTypes) => (
80
+ <div className="info-item" key={customField.id}>
81
+ <b>{customField.label} :</b> {renderCustomFieldValue(customField)}
82
+ </div>
83
+ ))}
84
+ </div>
85
+ {showHolderCustomFieldsSection && (
86
+ <div className="custom-fields-block show">
87
+ <p className="editable-ticket-id">Edit ticket {ticket.hash}</p>
88
+ <CustomFieldsForm
89
+ initialValues={holderEditableFieldsFormInitialValues}
90
+ fields={allowedHolderCustomFields}
91
+ handleFormSubmit={handleForSubmit}
92
+ handleFormClose={handleFormClose}
93
+ />
94
+ </div>
95
+ )}
96
+ </>
97
+ )
98
+ }
99
+
100
+ export { TicketHolderCustomFields }
@@ -11,15 +11,26 @@ import _find from 'lodash/find'
11
11
  import _get from 'lodash/get'
12
12
  import _has from 'lodash/has'
13
13
  import _identity from 'lodash/identity'
14
+ import _isEmpty from 'lodash/isEmpty'
14
15
  import _map from 'lodash/map'
15
16
  import moment from 'moment-timezone'
16
- import React, { useEffect, useState } from 'react'
17
+ import React, { useEffect, useMemo, useState } from 'react'
18
+ import SVG from 'react-inlinesvg'
17
19
 
18
- import { getOrderDetails, removeFromResale, resaleTicket } from '../../api'
20
+ import {
21
+ getOrderDetails,
22
+ removeFromResale,
23
+ resaleTicket,
24
+ updateOrderCustomFields,
25
+ updateTicketHoldersCustomFields,
26
+ } from '../../api'
27
+ import EditSvg from '../../assets/images/edit.svg'
19
28
  import { isBrowser } from '../../utils'
20
29
  import ConfirmModal from '../confirmModal'
21
30
  import { InitialValuesTypes, TicketResaleModal } from '../ticketResaleModal'
31
+ import { CustomFieldsForm } from './CustomFieldsForm'
22
32
  import TicketsTable, { IActionColumns, ITicketTypes } from './ticketsTable'
33
+ import { isFieldUpdatable, renderCustomFieldValue } from './utils'
23
34
 
24
35
  interface TicketTypes {
25
36
  currency: string;
@@ -43,6 +54,10 @@ interface OrderDetailsTypes {
43
54
  onRemoveFromResaleError: (err: any) => void;
44
55
  onResaleTicketSuccess: () => void;
45
56
  onResaleTicketError: (err: any) => void;
57
+ onUpdateOrderCustomFieldsSuccess: (val: any) => void;
58
+ onUpdateOrderCustomFieldsError: (err: any) => void;
59
+ onUpdateTicketHolderCustomFieldsSuccess: (val: any) => void;
60
+ onUpdateTicketHolderCustomFieldsError: (err: any) => void;
46
61
  personalLinkIcon?: string;
47
62
  displayColumnNameInRow?: boolean;
48
63
  canSellTicket?: boolean;
@@ -58,6 +73,33 @@ interface OrderDetailsTypes {
58
73
  ticketsTitle?: string;
59
74
  }
60
75
 
76
+ export interface CustomFieldOption {
77
+ id: string;
78
+ name: string;
79
+ value: string;
80
+ order: string;
81
+ }
82
+
83
+ export interface CustomFieldTypes {
84
+ description: string;
85
+ enabled: string;
86
+ id: string;
87
+ label: string;
88
+ name: string;
89
+ orderId: string;
90
+ required: boolean;
91
+ settings: {
92
+ showOnMyTickets: boolean;
93
+ allowUpdate: boolean;
94
+ className: string;
95
+ };
96
+ type: string;
97
+ value: string | Array<string>;
98
+
99
+ ticketHash?: string;
100
+ options?: CustomFieldOption[];
101
+ }
102
+
61
103
  const getTotal = (data: any) => {
62
104
  if (data?.total && data?.tickets && data.tickets[0]?.currency)
63
105
  return data.tickets[0].currency + data.total
@@ -74,6 +116,10 @@ export const OrderDetailsContainer = ({
74
116
  onRemoveFromResaleError = _identity,
75
117
  onResaleTicketSuccess = _identity,
76
118
  onResaleTicketError = _identity,
119
+ onUpdateOrderCustomFieldsSuccess = _identity,
120
+ onUpdateOrderCustomFieldsError = _identity,
121
+ onUpdateTicketHolderCustomFieldsSuccess = _identity,
122
+ onUpdateTicketHolderCustomFieldsError = _identity,
77
123
  onReturnButtonClick,
78
124
  personalLinkIcon = '',
79
125
  displayColumnNameInRow = false,
@@ -91,12 +137,54 @@ export const OrderDetailsContainer = ({
91
137
  const [showResaleModal, setShowResaleModal] = useState(false)
92
138
  const [showRemoveResaleModal, setShowRemoveResaleModal] = useState(false)
93
139
  const [activeTicket, setActiveTicket] = useState<any>(null)
140
+ const [showOrderCustomFieldsSection, setShowOrderCustomFieldsSection] = useState(false)
141
+
142
+ const isTable = _get(data, 'tickets[0].is_table', false)
143
+ const columnsProps = isTable
144
+ ? [
145
+ { label: 'Items' },
146
+ { label: 'Price' },
147
+ { label: 'Guests count' },
148
+ { label: 'Deposit paid' },
149
+ { label: 'Remaining' },
150
+ ]
151
+ : columns
152
+ const ticketHoldersCustomFields = _get(data, 'ticket_data_capture', [])
153
+
154
+ const orderCustomFields = _get(data, 'data_capture', [])
155
+ const [orderUpdatableCustomFields, orderNotUpdatableCustomFields] = useMemo(() => {
156
+ const allowedFields: CustomFieldTypes[] = []
157
+ const notAllowedFields: CustomFieldTypes[] = []
158
+
159
+ orderCustomFields.forEach((field: CustomFieldTypes) => {
160
+ if (isFieldUpdatable(field)) {
161
+ allowedFields.push(field)
162
+ } else {
163
+ notAllowedFields.push(field)
164
+ }
165
+ })
166
+ return [allowedFields, notAllowedFields]
167
+ }, [orderCustomFields])
168
+ const orderEditableFieldsFormInitialValues = useMemo(() => {
169
+ const initialValues = orderUpdatableCustomFields.map((field: CustomFieldTypes) => [
170
+ field.name,
171
+ field.type === 'checkbox' ? Boolean(field.value) : field.value || '',
172
+ ])
173
+ return Object.fromEntries(initialValues)
174
+ }, [orderUpdatableCustomFields])
175
+
94
176
  let orderId = String(pOrderId) || ''
95
177
  if (isBrowser && !pOrderId) {
96
178
  const params: URLSearchParams = new URL(`${window.location}`).searchParams
97
179
  orderId = params.get('o') || ''
98
180
  }
99
181
 
182
+ let orderSummery = `ID ${data.id}, placed`
183
+ if (data.date && data.timezone) {
184
+ const date = moment.tz(data.date, data.timezone).format('dddd, DD MMMM YYYY')
185
+ orderSummery += ` ${date}`
186
+ }
187
+
100
188
  useEffect(() => {
101
189
  (async () => {
102
190
  try {
@@ -213,25 +301,65 @@ export const OrderDetailsContainer = ({
213
301
  }
214
302
  }
215
303
 
216
- let orderSummery = `ID ${data.id}, placed`
217
- if (data.date && data.timezone) {
218
- const date = moment.tz(data.date, data.timezone).format('dddd, DD MMMM YYYY')
219
- orderSummery += ` ${date}`
304
+ const handleOrderCustomFieldsUpdate = async (values: Record<string, any>) => {
305
+ setShowOrderCustomFieldsSection(false)
306
+ try {
307
+ // Collect all updatable and non updatable values for bulk update
308
+ const fullUpdatableValues = orderNotUpdatableCustomFields.reduce(
309
+ (acc, customField) => {
310
+ acc[customField.name] = customField.value
311
+ return acc
312
+ },
313
+ { ...values }
314
+ )
315
+ const eventId = _get(data, 'event_id')
316
+ const response = await updateOrderCustomFields(
317
+ eventId,
318
+ orderId,
319
+ fullUpdatableValues
320
+ )
321
+ const updatedCustomFields = _get(response, 'data.data.attributes', [])
322
+
323
+ setData((prevState: Record<string, any>) => ({
324
+ ...prevState,
325
+ data_capture: updatedCustomFields,
326
+ }))
327
+ onUpdateOrderCustomFieldsSuccess(response)
328
+ } catch (error) {
329
+ onUpdateOrderCustomFieldsError(error)
330
+ }
331
+ }
332
+
333
+ const handleTicketHoldersUpdate = async (
334
+ values: Record<string, any>,
335
+ ticketHash: string
336
+ ) => {
337
+ try {
338
+ const eventId = _get(data, 'event_id')
339
+ const response = await updateTicketHoldersCustomFields(eventId, values, ticketHash)
340
+ const updatedFields = _get(response, 'data.data.attributes', [])
341
+ const notUpdatedFields = ticketHoldersCustomFields.filter(
342
+ (field: CustomFieldTypes) => field.ticketHash !== ticketHash
343
+ )
344
+
345
+ setData((prevState: Record<string, any>) => ({
346
+ ...prevState,
347
+ ticket_data_capture: [...updatedFields, ...notUpdatedFields],
348
+ }))
349
+ onUpdateTicketHolderCustomFieldsSuccess(response)
350
+ } catch (error) {
351
+ onUpdateTicketHolderCustomFieldsError(error)
352
+ }
220
353
  }
221
- const isTable = _get(data, 'tickets[0].is_table', false)
222
- const columnsProps = isTable
223
- ? [
224
- { label: 'Items' },
225
- { label: 'Price' },
226
- { label: 'Guests count' },
227
- { label: 'Deposit paid' },
228
- { label: 'Remaining' },
229
- ]
230
- : columns
231
354
 
232
355
  return (
233
356
  <div className="order-details">
234
- <h1 className="layout-title">Order Details</h1>
357
+ <div className="layout-block">
358
+ <h1 className="layout-title">Order Details</h1>
359
+ {!_isEmpty(orderUpdatableCustomFields) && (
360
+ <SVG src={EditSvg} onClick={() => setShowOrderCustomFieldsSection(true)} />
361
+ )}
362
+ </div>
235
363
  <div className="order-summary-box">
236
364
  <div className="summary-block">
237
365
  <div className="summary-item">
@@ -244,7 +372,7 @@ export const OrderDetailsContainer = ({
244
372
  type="button"
245
373
  className="return-button"
246
374
  onClick={() => {
247
- if (typeof window !== 'undefined') {
375
+ if (isBrowser) {
248
376
  window.location.assign(ordersPath ?? '/orders')
249
377
  }
250
378
  }}
@@ -254,6 +382,32 @@ export const OrderDetailsContainer = ({
254
382
  </div>
255
383
  </div>
256
384
  </div>
385
+ {!_isEmpty(orderCustomFields) && (
386
+ <>
387
+ <div className="custom-field-value-container">
388
+ {orderCustomFields.map((customField: CustomFieldTypes) => (
389
+ <div className="info-item" key={customField.id}>
390
+ <b>{customField.label} :</b> {renderCustomFieldValue(customField)}
391
+ </div>
392
+ ))}
393
+ </div>
394
+ {!_isEmpty(orderUpdatableCustomFields) && (
395
+ <div
396
+ className={`custom-fields-block ${
397
+ showOrderCustomFieldsSection ? 'show' : ''
398
+ }`}
399
+ >
400
+ <p className="title">Aditional Information Request</p>
401
+ <CustomFieldsForm
402
+ initialValues={orderEditableFieldsFormInitialValues}
403
+ fields={orderUpdatableCustomFields}
404
+ handleFormSubmit={handleOrderCustomFieldsUpdate}
405
+ handleFormClose={() => setShowOrderCustomFieldsSection(false)}
406
+ />
407
+ </div>
408
+ )}
409
+ </>
410
+ )}
257
411
  {!data?.disable_referral && (
258
412
  <>
259
413
  {referralTitle && (
@@ -352,6 +506,8 @@ export const OrderDetailsContainer = ({
352
506
  handleRemoveFromResale={handleRemoveFromResale}
353
507
  displayColumnNameInRow={displayColumnNameInRow}
354
508
  canSellTicket={canSellTicket}
509
+ ticketHoldersCustomFields={ticketHoldersCustomFields}
510
+ handleTicketHoldersUpdate={handleTicketHoldersUpdate}
355
511
  />
356
512
  <div className="return-button-container">
357
513
  <button
@@ -360,7 +516,7 @@ export const OrderDetailsContainer = ({
360
516
  onClick={() => {
361
517
  if (onReturnButtonClick) {
362
518
  onReturnButtonClick(data)
363
- } else if (typeof window !== 'undefined') {
519
+ } else if (isBrowser) {
364
520
  window.location.assign(ordersPath ?? '/orders')
365
521
  }
366
522
  }}
@@ -368,7 +524,6 @@ export const OrderDetailsContainer = ({
368
524
  Return to Order History
369
525
  </button>
370
526
  </div>
371
-
372
527
  {showResaleModal && (
373
528
  <TicketResaleModal
374
529
  ticket={activeTicket}
@@ -7,17 +7,21 @@ import TableContainer from '@mui/material/TableContainer'
7
7
  import TableHead from '@mui/material/TableHead'
8
8
  import TableRow from '@mui/material/TableRow'
9
9
  import _identity from 'lodash/identity'
10
+ import _isEmpty from 'lodash/isEmpty'
10
11
  import _map from 'lodash/map'
11
12
  import React, { Fragment, useState } from 'react'
12
13
 
13
14
  import { downloadPDF } from '../../utils'
14
15
  import SnackbarAlert from '../common/SnackbarAlert'
16
+ import { CustomFieldTypes } from './index'
17
+ import { TicketHolderCustomFields } from './TicketHolderCustomFields'
15
18
 
16
19
  interface IAddOnTypes {
17
20
  name: string;
18
21
  groupName: string;
19
22
  status: string;
20
23
  }
24
+
21
25
  export interface ITicketTypes {
22
26
  add_ons?: IAddOnTypes[];
23
27
  hash: string;
@@ -51,10 +55,12 @@ interface TicketsTableTypes {
51
55
  }>;
52
56
  handleSellTicket: (ticket: ITicketTypes) => void;
53
57
  handleRemoveFromResale: (ticket: ITicketTypes) => void;
58
+ handleTicketHoldersUpdate: (values: Record<string, any>, ticketHash: string) => void;
54
59
 
55
60
  icon?: string;
56
61
  displayColumnNameInRow?: boolean;
57
62
  ticketsTitle?: string;
63
+ ticketHoldersCustomFields?: Array<CustomFieldTypes>;
58
64
  }
59
65
 
60
66
  type PdfDownload = {
@@ -65,12 +71,14 @@ type PdfDownload = {
65
71
  const TicketsTable = ({
66
72
  tickets = [],
67
73
  columns = [],
68
- handleSellTicket = _identity,
69
- handleRemoveFromResale = _identity,
70
74
  icon = '',
71
75
  displayColumnNameInRow = false,
72
76
  canSellTicket = true,
73
77
  ticketsTitle = 'Your Tickets',
78
+ ticketHoldersCustomFields = [],
79
+ handleSellTicket = _identity,
80
+ handleRemoveFromResale = _identity,
81
+ handleTicketHoldersUpdate = _identity,
74
82
  }: TicketsTableTypes) => {
75
83
  const [pdfError, setPdfError] = useState<string | null>(null)
76
84
  const [pdfDownload, setPdfDownload] = useState<PdfDownload>({
@@ -113,11 +121,7 @@ const TicketsTable = ({
113
121
  }
114
122
  }}
115
123
  >
116
- {ticketIsDownloading ? (
117
- <CircularProgress size="22px" />
118
- ) : (
119
- 'Download'
120
- )}
124
+ {ticketIsDownloading ? <CircularProgress size="22px" /> : 'Download'}
121
125
  </span>
122
126
  </TableCell>
123
127
  )
@@ -185,35 +189,53 @@ const TicketsTable = ({
185
189
  </TableHead>
186
190
  )}
187
191
  <TableBody>
188
- {tickets.map((ticket: ITicketTypes, index: number) => (
189
- <Fragment key={index}>
190
- <TableRow>{getRow(ticket)}</TableRow>
191
- {!!ticket.add_ons?.length && (
192
- <TableRow>
193
- <TableCell colSpan={6}>
194
- <Table className="ticket-add-on-table">
195
- <TableHead>
196
- <TableRow>
197
- <TableCell>Add-On</TableCell>
198
- <TableCell>Status</TableCell>
199
- </TableRow>
200
- </TableHead>
201
- <TableBody>
202
- {ticket.add_ons.map(
203
- (add_on: IAddOnTypes, index: number) => (
192
+ {tickets.map((ticket: ITicketTypes, index: number) => {
193
+ const holderCustomFields = ticketHoldersCustomFields.filter(
194
+ (field: CustomFieldTypes) => field?.ticketHash === ticket?.hash
195
+ )
196
+
197
+ return (
198
+ <Fragment key={index}>
199
+ <TableRow>{getRow(ticket)}</TableRow>
200
+ {!_isEmpty(holderCustomFields) && (
201
+ <TableRow>
202
+ <TableCell colSpan={columns.length}>
203
+ <TicketHolderCustomFields
204
+ ticket={ticket}
205
+ holderCustomFields={holderCustomFields}
206
+ handleTicketHoldersUpdate={handleTicketHoldersUpdate}
207
+ />
208
+ </TableCell>
209
+ </TableRow>
210
+ )}
211
+ {!!ticket.add_ons?.length && (
212
+ <TableRow>
213
+ <TableCell colSpan={6}>
214
+ <Table className="ticket-add-on-table">
215
+ <TableHead>
216
+ <TableRow>
217
+ <TableCell>Add-On</TableCell>
218
+ <TableCell>Status</TableCell>
219
+ </TableRow>
220
+ </TableHead>
221
+ <TableBody>
222
+ {ticket.add_ons.map((add_on: IAddOnTypes, index: number) => (
204
223
  <TableRow key={index}>
205
- <TableCell>{add_on.groupName && `${add_on.groupName}: `} {add_on.name}</TableCell>
224
+ <TableCell>
225
+ {add_on.groupName && `${add_on.groupName}: `}{' '}
226
+ {add_on.name}
227
+ </TableCell>
206
228
  <TableCell>{add_on.status}</TableCell>
207
229
  </TableRow>
208
- )
209
- )}
210
- </TableBody>
211
- </Table>
212
- </TableCell>
213
- </TableRow>
214
- )}
215
- </Fragment>
216
- ))}
230
+ ))}
231
+ </TableBody>
232
+ </Table>
233
+ </TableCell>
234
+ </TableRow>
235
+ )}
236
+ </Fragment>
237
+ )
238
+ })}
217
239
  </TableBody>
218
240
  </Table>
219
241
  </TableContainer>
@@ -0,0 +1,55 @@
1
+ import _isArray from 'lodash/isArray'
2
+ import _isEmpty from 'lodash/isEmpty'
3
+
4
+ import { CustomFieldOption, CustomFieldTypes } from '../index'
5
+
6
+ const getLabelForValue = (options: CustomFieldOption[], value: string | string[]): string => {
7
+ const getLabel = (singleValue: string) => {
8
+ const option = options.find(option => option.value === singleValue)
9
+ return option ? option.name : ''
10
+ }
11
+
12
+ if (_isArray(value)) {
13
+ const labels = value.map(getLabel)
14
+ return labels.join(', ')
15
+ }
16
+
17
+ return getLabel(value as string)
18
+ }
19
+
20
+ export const renderCustomFieldValue = (customField: CustomFieldTypes) => {
21
+ if (customField.type === 'checkbox') {
22
+ return customField.value ? 'Checked' : 'Unchecked'
23
+ }
24
+
25
+ if (_isEmpty(customField.value)) {
26
+ return 'N/A'
27
+ }
28
+
29
+ if (customField.type === 'select' || customField.type === 'select_multi' || customField.type === 'radio') {
30
+ return getLabelForValue(customField.options as CustomFieldOption[], customField.value)
31
+ }
32
+
33
+ return customField.value
34
+ }
35
+
36
+ export const isFieldUpdatable = (field: CustomFieldTypes): boolean => {
37
+ if (!field?.settings?.allowUpdate || (field?.type === 'checkbox' && field?.required)) {
38
+ return false
39
+ }
40
+ return true
41
+ }
42
+
43
+ export const createCustomFieldsUpdateBody = (
44
+ updatedValues: Record<string, any>,
45
+ notUpdatableCustomFieldsValues: Array<CustomFieldTypes> = []
46
+ ) =>
47
+ notUpdatableCustomFieldsValues.reduce(
48
+ (acc, customField) => {
49
+ if (!isFieldUpdatable(customField)) {
50
+ acc[customField.name] = customField.value
51
+ }
52
+ return acc
53
+ },
54
+ { ...updatedValues }
55
+ )