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.
- package/dist/adapters/customFields.d.ts +11 -0
- package/dist/adapters/index.d.ts +1 -0
- package/dist/api/index.d.ts +6 -0
- package/dist/components/billing-info-container/index.d.ts +5 -2
- package/dist/components/billing-info-container/utils.d.ts +7 -5
- package/dist/components/common/Checkbox.d.ts +11 -0
- package/dist/components/common/CheckboxField/index.d.ts +9 -0
- package/dist/components/common/index.d.ts +1 -0
- package/dist/components/orderDetailsContainer/CustomFieldsForm.d.ts +12 -0
- package/dist/components/orderDetailsContainer/TicketHolderCustomFields.d.ts +10 -0
- package/dist/components/orderDetailsContainer/index.d.ts +29 -1
- package/dist/components/orderDetailsContainer/ticketsTable.d.ts +4 -1
- package/dist/components/orderDetailsContainer/utils/index.d.ts +6 -0
- package/dist/hoc/CustomFields/index.d.ts +3 -0
- package/dist/hoc/index.d.ts +1 -0
- package/dist/images/edit.svg +10 -0
- package/dist/tf-checkout-react.cjs.development.js +922 -210
- 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 +924 -212
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/billing-info-data.d.ts +2 -2
- package/dist/utils/createCheckoutDataBodyWithDefaultHolder.d.ts +3 -0
- package/dist/utils/customFields.d.ts +24 -0
- package/dist/utils/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/adapters/customFields.ts +79 -0
- package/src/adapters/index.ts +1 -0
- package/src/api/index.ts +43 -0
- package/src/assets/images/edit.svg +10 -0
- package/src/components/billing-info-container/index.tsx +106 -60
- package/src/components/billing-info-container/utils.ts +29 -5
- package/src/components/common/{CheckboxField.tsx → Checkbox.tsx} +3 -3
- package/src/components/common/CheckboxField/index.tsx +41 -0
- package/src/components/common/CustomField.tsx +1 -1
- package/src/components/common/RadioGroupField/index.tsx +1 -1
- package/src/components/common/SelectField/index.tsx +1 -1
- package/src/components/common/index.tsx +1 -0
- package/src/components/orderDetailsContainer/CustomFieldsForm.tsx +75 -0
- package/src/components/orderDetailsContainer/TicketHolderCustomFields.tsx +100 -0
- package/src/components/orderDetailsContainer/index.tsx +175 -20
- package/src/components/orderDetailsContainer/ticketsTable.tsx +55 -33
- package/src/components/orderDetailsContainer/utils/index.tsx +55 -0
- package/src/components/paymentContainer/PaymentPlanSection.tsx +52 -35
- package/src/components/paymentContainer/index.tsx +2 -2
- package/src/components/stripePayment/index.tsx +2 -2
- package/src/components/ticketResale/index.tsx +4 -1
- package/src/components/ticketsContainer/index.tsx +1 -0
- package/src/hoc/CustomFields/index.tsx +77 -0
- package/src/hoc/index.ts +1 -0
- package/src/types/billing-info-data.ts +2 -2
- package/src/utils/createCheckoutDataBodyWithDefaultHolder.ts +3 -0
- package/src/utils/customFields.ts +58 -0
- package/src/utils/index.ts +1 -0
- package/dist/components/common/CheckboxField.d.ts +0 -11
|
@@ -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 {
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
<
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
{
|
|
203
|
-
|
|
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>
|
|
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
|
-
</
|
|
211
|
-
</
|
|
212
|
-
</
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
+
)
|