@openeventkit/event-site 2.0.131 → 2.0.133

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openeventkit/event-site",
3
3
  "description": "Event Site",
4
- "version": "2.0.131",
4
+ "version": "2.0.133",
5
5
  "author": "Tipit LLC",
6
6
  "dependencies": {
7
7
  "@fortawesome/fontawesome-svg-core": "^6.5.2",
@@ -143,8 +143,6 @@ export const fetchSummitById = async(summitId, accessToken = null) => {
143
143
  let apiUrl = URI(`${process.env.GATSBY_SUMMIT_API_BASE_URL}/api/public/v1/summits/${summitId}`);
144
144
 
145
145
  apiUrl.addQuery('expand', 'event_types,tracks,track_groups,presentation_levels,locations.rooms,locations.floors,order_extra_questions.values,schedule_settings,schedule_settings.filters,schedule_settings.pre_filters');
146
- apiUrl.addQuery('t', Date.now());
147
- apiUrl.addQuery('evict_cache', 1);
148
146
 
149
147
  return fetch(apiUrl.toString(), {
150
148
  method: 'GET'
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Copyright 2022
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ * http://www.apache.org/licenses/LICENSE-2.0
7
+ * Unless required by applicable law or agreed to in writing, software
8
+ * distributed under the License is distributed on an "AS IS" BASIS,
9
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ * See the License for the specific language governing permissions and
11
+ * limitations under the License.
12
+ **/
13
+ import React, { useState } from 'react'
14
+ import { useTranslation } from "react-i18next";
15
+ import { Input } from 'openstack-uicore-foundation/lib/components';
16
+ import usePortal from 'react-useportal';
17
+
18
+ import './delegate-popup.scss';
19
+
20
+ export const DelegatePopup = ({ isOpen, onAccept, onReject }) => {
21
+ var { Portal } = usePortal();
22
+ const { t } = useTranslation();
23
+
24
+ const handleAcceptClick = () => {
25
+ if (onAccept) onAccept();
26
+ };
27
+
28
+ const handleRejectClick = () => {
29
+ if (onReject) onReject();
30
+ };
31
+
32
+ return (
33
+ <>
34
+ {isOpen && (
35
+ <Portal>
36
+ <div className="confirm-popup-bg">
37
+ <div className="confirm-popup">
38
+ <h4>Delegate Ticket</h4>
39
+ <p>Delegation will create a new attendee and require to you to fill the first Name, last Name and to answer the extra questions again</p>
40
+
41
+ <div className="buttons">
42
+ <span onClick={handleRejectClick}>{t("confirm_popup.cancel")}</span>
43
+ <span onClick={handleAcceptClick}>{t("confirm_popup.accept")}</span>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ </Portal>
48
+ )}
49
+ </>
50
+ );
51
+ };
@@ -0,0 +1,57 @@
1
+ @import "../../styles/variables";
2
+
3
+ .confirm-popup-bg {
4
+ z-index: 1000;
5
+ position: fixed;
6
+ width: 100%;
7
+ height: 100%;
8
+ top: 0;
9
+ left: 0;
10
+ right: 0;
11
+ bottom: 0;
12
+ margin: auto;
13
+ }
14
+
15
+ .confirm-popup {
16
+ position: absolute;
17
+ right: 1%;
18
+ top: 5%;
19
+ padding: 40px 20px 20px;
20
+ background: $color-white;
21
+ z-index: 35;
22
+ text-align: center;
23
+ box-shadow: 1px 1px 5px grey;
24
+ min-width: 300px;
25
+
26
+ h4 {
27
+ font-weight: bold;
28
+ }
29
+
30
+ p {
31
+ margin: 0 40px 20px;
32
+ }
33
+
34
+ .buttons {
35
+ margin-top: 35px;
36
+ display: flex;
37
+ justify-content: space-evenly;
38
+ align-items: baseline;
39
+
40
+ span {
41
+ cursor: pointer;
42
+ }
43
+
44
+ span:last-child {
45
+ background: $color-secondary-contrast;
46
+ border-radius: 4px;
47
+ color: $color-white;
48
+ padding: 5px 30px;
49
+ text-align: center;
50
+
51
+ &:hover {
52
+ background-color: #286090;
53
+ border-color: #204d74;
54
+ }
55
+ }
56
+ }
57
+ }
@@ -35,7 +35,7 @@ export const OrderListItem = ({ order, className, changeTicketsPage }) => {
35
35
  <OrderSummary type="mobile" order={order} summit={summit} tickets={tickets}/>
36
36
 
37
37
  {summit.ticket_types.map((ticketType, index) => {
38
- const orderTickets = tickets.filter(t => t.ticket_type_id == ticketType.id);
38
+ const orderTickets = tickets.filter(t => t.ticket_type.id == ticketType.id);
39
39
 
40
40
  if (orderTickets.length < 1) return null;
41
41
 
@@ -12,15 +12,18 @@ import { getMainOrderExtraQuestions } from '../../../store/actions/summit-action
12
12
  import {
13
13
  editOwnedTicket,
14
14
  removeAttendee,
15
+ delegateTicket,
15
16
  TICKET_ATTENDEE_KEYS as TicketKeys
16
17
  } from '../../../store/actions/ticket-actions';
17
18
  import { useTicketDetails } from '../../../util';
19
+ import useMarketingSettings, { MARKETING_SETTINGS_KEYS } from "@utils/useMarketingSettings";
18
20
  import { ConfirmPopup, CONFIRM_POPUP_CASE } from '../../ConfirmPopup/ConfirmPopup';
19
21
 
20
22
  import { DefaultScrollBehaviour as ScrollBehaviour } from '@utils/scroll';
21
23
 
22
24
  import './ticket-popup-edit-details-form.scss';
23
25
  import { useTicketAssignedContext } from '../../../context/TicketAssignedContext';
26
+ import { DelegatePopup } from '../../DelegatePopup/DelegatePopup';
24
27
 
25
28
  const noop = () => {};
26
29
 
@@ -41,15 +44,24 @@ export const TicketPopupEditDetailsForm = ({
41
44
  const [triedSubmitting, setTriedSubmitting] = useState(false);
42
45
  const [showSaveMessage, setShowSaveMessage] = useState(false);
43
46
  const [showConfirm, setShowConfirm] = useState(false);
47
+ const [showDelegate, setShowDelegate] = useState(false);
48
+ const [isDelegating, setIsDelegating] = useState(false);
44
49
  const [showUnassignMessage, setShowUnassignMessage] = useState(false);
45
50
  const {
46
51
  isReassignable,
47
52
  formattedReassignDate,
48
- daysUntilReassignDeadline
49
- } = useTicketDetails({ ticket, summit });
53
+ daysUntilReassignDeadline,
54
+ isUnassigned,
55
+ allowsDelegate
56
+ } = useTicketDetails({ ticket, summit });
50
57
 
51
58
  const { onTicketAssignChange } = useTicketAssignedContext();
52
59
 
60
+ const { getSettingByKey } = useMarketingSettings();
61
+
62
+ const showCompanyInputDefaultOptions = !!Number(getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteShowCompanyInputDefaultOptions));
63
+ const showCompanyInput = !!Number(getSettingByKey(MARKETING_SETTINGS_KEYS.regLiteShowCompanyInput));
64
+
53
65
  const initialValues = useMemo(() => {
54
66
  const {
55
67
  email,
@@ -78,7 +90,7 @@ export const TicketPopupEditDetailsForm = ({
78
90
  const validationSchema = useMemo(() => Yup.object().shape({
79
91
  [TicketKeys.firstName]: Yup.string().required(),
80
92
  [TicketKeys.lastName]: Yup.string().required(),
81
- [TicketKeys.company]: Yup.object().shape({
93
+ [TicketKeys.company]: showCompanyInput && Yup.object().shape({
82
94
  id: Yup.number().nullable(),
83
95
  name: Yup.string().required(),
84
96
  }),
@@ -115,14 +127,26 @@ export const TicketPopupEditDetailsForm = ({
115
127
  data: values,
116
128
  };
117
129
 
118
- dispatch(editOwnedTicket(params))
119
- .then(() => toggleSaveMessage())
120
- .catch((error) => console.error(error))
121
- .then(() => {
122
- // Note: Need to do this to persist the extra question values
123
- formikHelpers.resetForm({ values });
124
- formikHelpers.setSubmitting(false);
125
- });
130
+ if(isDelegating) {
131
+ dispatch(delegateTicket(params))
132
+ .then(() => toggleSaveMessage())
133
+ .catch((error) => console.error(error))
134
+ .then(() => {
135
+ formikHelpers.resetForm({ values });
136
+ formikHelpers.setSubmitting(false);
137
+ setIsDelegating(false);
138
+ });
139
+ } else {
140
+ dispatch(editOwnedTicket(params))
141
+ .then(() => toggleSaveMessage())
142
+ .catch((error) => console.error(error))
143
+ .then(() => {
144
+ // Note: Need to do this to persist the extra question values
145
+ formikHelpers.resetForm({ values });
146
+ formikHelpers.setSubmitting(false);
147
+ });
148
+ }
149
+
126
150
  };
127
151
 
128
152
  const handleConfirmAccept = async () => {
@@ -137,6 +161,24 @@ export const TicketPopupEditDetailsForm = ({
137
161
  setShowConfirm(false);
138
162
  };
139
163
 
164
+ const handleDelegateAccept = () => {
165
+ setShowDelegate(false);
166
+ setIsDelegating(true);
167
+ formik.resetForm({
168
+ values: {
169
+ ...formik.values,
170
+ [TicketKeys.firstName]: "",
171
+ [TicketKeys.lastName]: "",
172
+ [TicketKeys.disclaimerAccepted]: false,
173
+ [TicketKeys.extraQuestions]: []
174
+ }
175
+ });
176
+ };
177
+
178
+ const handleDelegateReject = () => {
179
+ setShowDelegate(false);
180
+ };
181
+
140
182
  const handleSubmit = (values, formikHelpers) => updateTicket(values, formikHelpers);
141
183
 
142
184
  const formik = useFormik({
@@ -209,7 +251,7 @@ export const TicketPopupEditDetailsForm = ({
209
251
  const canSubmitChanges = () => {
210
252
  const qs = new QuestionsSet(extraQuestions, initialValues[TicketKeys.extraQuestions]);
211
253
  const unansweredExtraQuestions = !qs.completed();
212
- return canEditTicketData || isReassignable || unansweredExtraQuestions;
254
+ return canEditTicketData || isReassignable || unansweredExtraQuestions || isDelegating;
213
255
  }
214
256
 
215
257
  return (
@@ -277,8 +319,8 @@ export const TicketPopupEditDetailsForm = ({
277
319
  placeholder={t("ticket_popup.edit_first_name_placeholder")}
278
320
  value={formik.values[TicketKeys.firstName]}
279
321
  onBlur={formik.handleBlur}
280
- onChange={!!initialValues[TicketKeys.firstName] ? noop : formik.handleChange}
281
- disabled={!!initialValues[TicketKeys.firstName]}
322
+ onChange={!!initialValues[TicketKeys.firstName] && !isDelegating ? noop : formik.handleChange}
323
+ disabled={!!initialValues[TicketKeys.firstName] && !isDelegating}
282
324
  />
283
325
  {(formik.touched[TicketKeys.firstName] || triedSubmitting) && formik.errors[TicketKeys.firstName] &&
284
326
  <p className="error-label">{t("ticket_popup.edit_required")}</p>
@@ -297,39 +339,50 @@ export const TicketPopupEditDetailsForm = ({
297
339
  placeholder={t("ticket_popup.edit_last_name_placeholder")}
298
340
  value={formik.values[TicketKeys.lastName]}
299
341
  onBlur={formik.handleBlur}
300
- onChange={!!initialValues[TicketKeys.lastName] ? noop : formik.handleChange}
301
- disabled={!!initialValues[TicketKeys.lastName]}
342
+ onChange={!!initialValues[TicketKeys.lastName] && !isDelegating ? noop : formik.handleChange}
343
+ disabled={!!initialValues[TicketKeys.lastName] && !isDelegating}
302
344
  />
303
345
  {(formik.touched[TicketKeys.lastName] || triedSubmitting) && formik.errors[TicketKeys.lastName] &&
304
346
  <p className="error-label">{t("ticket_popup.edit_required")}</p>
305
347
  }
306
348
  </div>
307
349
 
308
- <div className="attendee-info column is-full">
309
- <label htmlFor={TicketKeys.company}>
310
- {t("ticket_popup.edit_company")}
311
- {!initialValues[TicketKeys.company].name && <b> *</b>}
312
- </label>
313
- <RegistrationCompanyInput
314
- id={TicketKeys.company}
315
- name={TicketKeys.company}
316
- summitId={summit.id}
317
- placeholder={t("ticket_popup.edit_company_placeholder")}
318
- value={formik.values[TicketKeys.company]}
319
- onBlur={formik.handleBlur}
320
- onChange={!!initialValues[TicketKeys.company].name ? noop : formik.handleChange}
321
- disabled={!!initialValues[TicketKeys.company].name}
322
- menuPortalTarget={document.body}
323
- menuPosition="fixed"
324
- styles={{
325
- menuPortal: (base) => ({ ...base, zIndex: 9999 }),
326
- }}
327
- tabSelectsValue={false}
328
- />
329
- {(formik.touched[TicketKeys.company] || triedSubmitting) && formik.errors[TicketKeys.company] &&
330
- <p className="error-label">{t("ticket_popup.edit_required")}</p>
331
- }
332
- </div>
350
+ {allowsDelegate &&
351
+ <div className="attendee-info column is-full">
352
+ <button className="button-text" type='button' onClick={() => setShowDelegate(true)}>Delegate</button>
353
+ </div>
354
+ }
355
+
356
+ {showCompanyInput &&
357
+ <div className="attendee-info column is-full">
358
+ <label htmlFor={TicketKeys.company}>
359
+ {t("ticket_popup.edit_company")}
360
+ {!initialValues[TicketKeys.company].name && <b> *</b>}
361
+ </label>
362
+ <RegistrationCompanyInput
363
+ id={TicketKeys.company}
364
+ name={TicketKeys.company}
365
+ summitId={summit.id}
366
+ placeholder={t("ticket_popup.edit_company_placeholder")}
367
+ value={formik.values[TicketKeys.company]}
368
+ onBlur={formik.handleBlur}
369
+ onChange={!!initialValues[TicketKeys.company].name ? noop : formik.handleChange}
370
+ disabled={!!initialValues[TicketKeys.company].name}
371
+ defaultOptions={showCompanyInputDefaultOptions}
372
+ openMenuOnFocus={showCompanyInputDefaultOptions}
373
+ openMenuOnClick={showCompanyInputDefaultOptions}
374
+ menuPortalTarget={document.body}
375
+ menuPosition="fixed"
376
+ styles={{
377
+ menuPortal: (base) => ({ ...base, zIndex: 9999 }),
378
+ }}
379
+ tabSelectsValue={false}
380
+ />
381
+ {(formik.touched[TicketKeys.company] || triedSubmitting) && formik.errors[TicketKeys.company] &&
382
+ <p className="error-label">{t("ticket_popup.edit_required")}</p>
383
+ }
384
+ </div>
385
+ }
333
386
 
334
387
  {(initialValues[TicketKeys.firstName] || initialValues[TicketKeys.lastName] || initialValues[TicketKeys.company].name) &&
335
388
  <div className="column is-full pb-5">
@@ -341,11 +394,13 @@ export const TicketPopupEditDetailsForm = ({
341
394
  <div className="column is-full pt-5">
342
395
  <h4 className="pb-2">{t("ticket_popup.edit_preferences")}</h4>
343
396
  <ExtraQuestionsForm
397
+ // This key is added to trigger a new render when the extra questions are resetted on delegation
398
+ key={formik.values[TicketKeys.extraQuestions].length}
344
399
  ref={formRef}
345
400
  extraQuestions={extraQuestions}
346
401
  userAnswers={formik.values[TicketKeys.extraQuestions]}
347
402
  onAnswerChanges={onExtraQuestionsAnswersSet}
348
- allowExtraQuestionsEdit={canEditTicketData}
403
+ allowExtraQuestionsEdit={canEditTicketData || isDelegating}
349
404
  questionContainerClassName={`columns is-multiline extra-question pt-3`}
350
405
  questionLabelContainerClassName={'column is-full pb-0'}
351
406
  questionControlContainerClassName={`column is-full pt-0`}
@@ -403,6 +458,11 @@ export const TicketPopupEditDetailsForm = ({
403
458
  onAccept={handleConfirmAccept}
404
459
  onReject={handleConfirmReject}
405
460
  />
461
+ <DelegatePopup
462
+ isOpen={showDelegate}
463
+ onAccept={handleDelegateAccept}
464
+ onReject={handleDelegateReject}
465
+ />
406
466
  </div>
407
467
  );
408
468
  };
@@ -38,6 +38,7 @@ export const GET_TICKETS_BY_ORDER = 'GET_TICKETS_BY_ORDER';
38
38
  export const GET_TICKETS_BY_ORDER_ERROR = 'GET_TICKETS_BY_ORDER_ERROR';
39
39
  export const GET_ORDER_TICKET_DETAILS = 'GET_ORDER_TICKET_DETAILS';
40
40
  export const GET_TICKET_DETAILS = 'GET_TICKET_DETAILS';
41
+ export const DELEGATE_TICKET = 'DELEGATE_TICKET';
41
42
 
42
43
  export const TICKET_ATTENDEE_KEYS = {
43
44
  email: 'attendee_email',
@@ -90,9 +91,9 @@ export const getUserTickets = ({ page = 1, perPage = 5 }) => async (dispatch, ge
90
91
 
91
92
  const params = {
92
93
  access_token: accessToken,
93
- expand: 'order, owner',
94
+ expand: 'order,owner,owner.manager,promo_code,ticket_type',
94
95
  order: '-id',
95
- fields: 'order.id,order.owner_first_name,order.owner_last_name,order.owner_email,owner.first_name,owner.last_name,owner.status',
96
+ fields: 'order.id,order.owner_first_name,order.owner_last_name,order.owner_email,owner.first_name,owner.last_name,owner.email,owner.company,owner.status,owner.manager.id',
96
97
  'filter[]': [`status==Paid`, `order_owner_id<>${userProfile.id}`],
97
98
  relations: 'none',
98
99
  page: page,
@@ -130,7 +131,7 @@ export const getTicketById = ({order, ticket}) => async (dispatch, getState, { g
130
131
 
131
132
  const params = {
132
133
  access_token: accessToken,
133
- expand: `${fromOrderList ? 'order' : ''}, owner, owner.extra_questions, badge, badge.features, refund_requests`,
134
+ expand: `${fromOrderList ? 'order' : ''},owner,owner.extra_questions,badge,badge.features,refund_requests,promo_code,ticket_type`,
134
135
  fields: 'order.id, order.owner_first_name, order.owner_last_name, order.owner_email'
135
136
  };
136
137
 
@@ -148,16 +149,16 @@ export const getTicketById = ({order, ticket}) => async (dispatch, getState, { g
148
149
  };
149
150
 
150
151
  export const getTicketsByOrder = ({ orderId, page = 1, perPage = 5 }) => async (dispatch, getState, { getAccessToken, apiBaseUrl, loginUrl }) => {
151
-
152
+
152
153
  dispatch(startLoading());
153
-
154
+
154
155
  const accessToken = await getAccessToken().catch(_ => history.replace(loginUrl));
155
156
 
156
157
  if (!accessToken) return;
157
158
 
158
159
  const params = {
159
- access_token: accessToken,
160
- expand: 'refund_requests, owner, owner.extra_questions, badge, badge.features',
160
+ access_token: accessToken,
161
+ expand: 'refund_requests,owner,owner.extra_questions,badge,badge.features,promo_code,ticket_type',
161
162
  order: '+id',
162
163
  page: page,
163
164
  per_page: perPage
@@ -166,7 +167,7 @@ export const getTicketsByOrder = ({ orderId, page = 1, perPage = 5 }) => async (
166
167
  return getRequest(
167
168
  null,
168
169
  createAction(GET_TICKETS_BY_ORDER),
169
- `${apiBaseUrl}/api/v1/summits/all/orders/${orderId}/tickets`,
170
+ `${apiBaseUrl}/api/v1/summits/all/orders/${orderId}/tickets`,
170
171
  authErrorHandler
171
172
  )(params)(dispatch).then(() => {
172
173
  dispatch(stopLoading());
@@ -300,10 +301,12 @@ export const editOwnedTicket = ({
300
301
  `${apiBaseUrl}/api/v1/summits/all/orders/all/tickets/${ticket.id}`,
301
302
  normalizedEntity,
302
303
  authErrorHandler
303
- )(params)(dispatch).then(async () => {
304
+ )(params)(dispatch).then(async () => {
305
+ const hasManager = ticket.owner?.manager?.id || ticket.owner?.manager_id;
304
306
  // email should match ( only update my profile is ticket belongs to me!)
307
+ // and if the ticket doesn't have a manager
305
308
  // Check if there's changes in the ticket data to update the profile
306
- if (userProfile.email == attendee_email && (
309
+ if (userProfile.email == attendee_email && !hasManager && (
307
310
  attendee_company.name !== company ||
308
311
  attendee_first_name !== userProfile.first_name ||
309
312
  attendee_last_name !== userProfile.last_name)) {
@@ -314,7 +317,6 @@ export const editOwnedTicket = ({
314
317
  company: attendee_company.name
315
318
  };
316
319
  dispatch(updateProfile(newProfile));
317
-
318
320
  }
319
321
 
320
322
  // Note: make sure we re-fetch the user profile for the parent app.
@@ -324,7 +326,7 @@ export const editOwnedTicket = ({
324
326
  if (context === 'ticket-list') {
325
327
  dispatch(getUserTickets({ page: ticketPage }));
326
328
  } else {
327
- dispatch(getUserOrders({ page: orderPage })).then(() =>
329
+ dispatch(getUserOrders({ page: orderPage })).then(() =>
328
330
  dispatch(getTicketsByOrder({ orderId: ticket.order_id, page: orderTicketsCurrentPage }))
329
331
  );
330
332
  }
@@ -529,6 +531,67 @@ export const refundTicket = ({ ticket, order }) => async (dispatch, getState, {
529
531
  });
530
532
  };
531
533
 
534
+ export const delegateTicket = ({
535
+ ticket,
536
+ context,
537
+ data: {
538
+ attendee_email,
539
+ attendee_first_name,
540
+ attendee_last_name,
541
+ attendee_company,
542
+ disclaimer_accepted,
543
+ extra_questions,
544
+ }
545
+ }) => async (dispatch, getState, { getAccessToken, apiBaseUrl, loginUrl }) => {
546
+ const accessToken = await getAccessToken().catch(_ => history.replace(loginUrl));
547
+
548
+ if (!accessToken) return;
549
+
550
+ dispatch(startLoading());
551
+
552
+ const {
553
+ orderState: { current_page: orderPage },
554
+ ticketState: { current_page: ticketPage, orderTickets: { current_page : orderTicketsCurrentPage }},
555
+ summitState: { summit: { id : summitId } }
556
+ } = getState();
557
+
558
+ const orderId = ticket.order ? ticket.order.id : ticket.order_id;
559
+
560
+ const params = {
561
+ access_token: accessToken
562
+ };
563
+
564
+ const normalizedEntity = normalizeTicket({
565
+ attendee_email,
566
+ attendee_first_name,
567
+ attendee_last_name,
568
+ attendee_company,
569
+ disclaimer_accepted,
570
+ extra_questions
571
+ });
572
+
573
+ return putRequest(
574
+ null,
575
+ createAction(DELEGATE_TICKET),
576
+ `${apiBaseUrl}/api/v1/summits/${summitId}/orders/${orderId}/tickets/${ticket.id}/delegate`,
577
+ normalizedEntity,
578
+ authErrorHandler
579
+ )(params)(dispatch).then(() => {
580
+ dispatch(stopLoading());
581
+ // Note: refresh the list view after updating the ticket.
582
+ if (context === 'ticket-list') {
583
+ dispatch(getUserTickets({ page: ticketPage }));
584
+ } else {
585
+ dispatch(getUserOrders({ page: orderPage })).then(() =>
586
+ dispatch(getTicketsByOrder({ orderId: ticket.order_id, page: orderTicketsCurrentPage }))
587
+ );
588
+ }
589
+ }).catch(e => {
590
+ dispatch(stopLoading());
591
+ throw (e);
592
+ });
593
+ }
594
+
532
595
  const normalizeTicket = (entity) => {
533
596
  const normalizedEntity = {...entity};
534
597
 
@@ -541,4 +604,4 @@ const normalizeTicket = (entity) => {
541
604
 
542
605
  return normalizedEntity;
543
606
 
544
- }
607
+ }
@@ -19,12 +19,14 @@ export const useTicketDetails = ({ ticket, summit }) => {
19
19
 
20
20
  const status = getTicketStatusData(ticket, isPast);
21
21
  const role = getTicketRole(ticket);
22
- const type = summit.ticket_types.find(type => type.id == ticket.ticket_type_id);
22
+ const type = summit.ticket_types.find(type => type.id == ticket.ticket_type.id);
23
23
 
24
24
  const isActive = ticket.is_active && status.type !== STATUS_CANCELLED;
25
25
  const isUnassigned = status.type === STATUS_UNASSIGNED;
26
26
  const isRefundable = ticket.final_amount > 0 && ticket.final_amount > ticket.refunded_amount;
27
27
 
28
+ const allowsDelegate = (ticket.ticket_type.allows_to_delegate || ticket.promo_code?.allows_to_delegate) && !isUnassigned && !ticket.owner?.manager?.id && !ticket.owner?.manager_id;
29
+
28
30
  const togglePopup = () => setShowPopup(!showPopup);
29
31
 
30
32
  const handleClick = () => {
@@ -45,6 +47,7 @@ export const useTicketDetails = ({ ticket, summit }) => {
45
47
  isUnassigned,
46
48
  isReassignable,
47
49
  isRefundable,
50
+ allowsDelegate,
48
51
  formattedDate,
49
52
  formattedReassignDate,
50
53
  daysUntilReassignDeadline,
@@ -5,10 +5,16 @@
5
5
 
6
6
  @import 'colors.scss';
7
7
 
8
+ .modal {
9
+ z-index: 100000;
10
+ background-color: rgba(0,0,0,0.8);
11
+ }
12
+
8
13
  .modal-card {
9
14
  background-color: var(--color_background_light);
10
15
  color: var(--color_text_dark);
11
16
  margin: 0 5rem 0 auto;
17
+ border: 1px solid var(--color_input_border_color_dark);
12
18
 
13
19
  .modal-card-head {
14
20
  padding: 10px 20px;
@@ -506,4 +506,14 @@ div.event-feedback-container {
506
506
  }
507
507
  }
508
508
 
509
+ .button-text {
510
+ border: none;
511
+ background-color: transparent;
512
+ cursor: pointer;
513
+ font-weight: bold;
514
+ &:hover {
515
+ text-decoration: underline;
516
+ }
517
+ }
518
+
509
519
  @import "~openstack-uicore-foundation/lib/css/components/extra-questions.css";