summit-registration-lite 7.0.1 → 7.0.2

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.
@@ -25,6 +25,7 @@ __webpack_require__.d(__webpack_exports__, {
25
25
  "t$": () => (/* binding */ CLEAR_WIDGET_STATE),
26
26
  "rr": () => (/* binding */ CREATE_RESERVATION_SUCCESS),
27
27
  "bz": () => (/* binding */ DELETE_RESERVATION_SUCCESS),
28
+ "dQ": () => (/* binding */ DISCOVER_PROMO_CODES_SUCCESS),
28
29
  "Cw": () => (/* binding */ GET_MY_INVITATION),
29
30
  "bx": () => (/* binding */ GET_TAX_TYPES),
30
31
  "HC": () => (/* binding */ GET_TICKET_TYPES),
@@ -40,9 +41,14 @@ __webpack_require__.d(__webpack_exports__, {
40
41
  "Vx": () => (/* binding */ START_WIDGET_LOADING),
41
42
  "sC": () => (/* binding */ STOP_WIDGET_LOADING),
42
43
  "VH": () => (/* binding */ UPDATE_CLOCK),
44
+ "j6": () => (/* binding */ VALIDATE_PROMO_CODE),
45
+ "o5": () => (/* binding */ VALIDATE_PROMO_CODE_ERROR),
46
+ "Tk": () => (/* binding */ VALIDATE_PROMO_CODE_RATE_LIMITED),
47
+ "w6": () => (/* binding */ VALIDATE_PROMO_CODE_SUCCESS),
43
48
  "gs": () => (/* binding */ applyPromoCode),
44
49
  "sz": () => (/* binding */ changeStep),
45
50
  "YS": () => (/* binding */ clearWidgetState),
51
+ "kM": () => (/* binding */ discoverPromoCodes),
46
52
  "aH": () => (/* binding */ getLoginCode),
47
53
  "q1": () => (/* binding */ getMyInvitation),
48
54
  "xu": () => (/* binding */ getTicketTypesAndTaxes),
@@ -61,7 +67,7 @@ __webpack_require__.d(__webpack_exports__, {
61
67
  "jn": () => (/* binding */ validatePromoCode)
62
68
  });
63
69
 
64
- // UNUSED EXPORTS: CREATE_RESERVATION, CREATE_RESERVATION_ERROR, DELETE_RESERVATION, DELETE_RESERVATION_ERROR, VALIDATE_PROMO_CODE
70
+ // UNUSED EXPORTS: CREATE_RESERVATION, CREATE_RESERVATION_ERROR, DELETE_RESERVATION, DELETE_RESERVATION_ERROR, DISCOVER_PROMO_CODES
65
71
 
66
72
  ;// CONCATENATED MODULE: external "openstack-uicore-foundation/lib/utils/actions"
67
73
  const actions_namespaceObject = require("openstack-uicore-foundation/lib/utils/actions");
@@ -397,6 +403,11 @@ const LOAD_PROFILE_DATA = 'LOAD_PROFILE_DATA';
397
403
  const SET_CURRENT_PROMO_CODE = 'SET_CURRENT_PROMO_CODE';
398
404
  const CLEAR_CURRENT_PROMO_CODE = 'CLEAR_CURRENT_PROMO_CODE';
399
405
  const VALIDATE_PROMO_CODE = 'VALIDATE_PROMO_CODE';
406
+ const VALIDATE_PROMO_CODE_SUCCESS = 'VALIDATE_PROMO_CODE_SUCCESS';
407
+ const VALIDATE_PROMO_CODE_ERROR = 'VALIDATE_PROMO_CODE_ERROR';
408
+ const VALIDATE_PROMO_CODE_RATE_LIMITED = 'VALIDATE_PROMO_CODE_RATE_LIMITED';
409
+ const DISCOVER_PROMO_CODES = 'DISCOVER_PROMO_CODES';
410
+ const DISCOVER_PROMO_CODES_SUCCESS = 'DISCOVER_PROMO_CODES_SUCCESS';
400
411
  const startWidgetLoading = (0,actions_namespaceObject.createAction)(START_WIDGET_LOADING);
401
412
  const stopWidgetLoading = (0,actions_namespaceObject.createAction)(STOP_WIDGET_LOADING);
402
413
  const loadSession = settings => dispatch => {
@@ -409,6 +420,23 @@ const clearWidgetState = () => dispatch => {
409
420
  dispatch((0,actions_namespaceObject.createAction)(CLEAR_WIDGET_STATE)({}));
410
421
  };
411
422
 
423
+ const promoCodeErrorHandler = (err, res) => (dispatch, state) => {
424
+ // 404: promo code or ticket type not found
425
+ // 412: promo code invalid for this ticket type/qty
426
+ if (res && [404, 412].includes(res.statusCode)) {
427
+ dispatch((0,actions_namespaceObject.createAction)(VALIDATE_PROMO_CODE_ERROR)({}));
428
+ return;
429
+ } // 429: rate limited - transient, preserve current promo state
430
+
431
+
432
+ if (res && res.statusCode === 429) {
433
+ dispatch((0,actions_namespaceObject.createAction)(VALIDATE_PROMO_CODE_RATE_LIMITED)({}));
434
+ return;
435
+ }
436
+
437
+ return (0,actions_namespaceObject.authErrorHandler)(err, res)(dispatch, state);
438
+ };
439
+
412
440
  const customErrorHandler = (err, res) => (dispatch, state) => {
413
441
  if (err.timeout) {
414
442
  return err;
@@ -426,13 +454,35 @@ const customErrorHandler = (err, res) => (dispatch, state) => {
426
454
  };
427
455
  /*********************************************************************************/
428
456
 
457
+ /* PROMO CODE DISCOVERY */
458
+
459
+ /*********************************************************************************/
460
+
461
+
462
+ const discoverPromoCodes = summitId => async (dispatch, getState, {
463
+ apiBaseUrl,
464
+ getAccessToken
465
+ }) => {
466
+ try {
467
+ const accessToken = await getAccessToken();
468
+ return (0,actions_namespaceObject.getRequest)((0,actions_namespaceObject.createAction)(DISCOVER_PROMO_CODES), (0,actions_namespaceObject.createAction)(DISCOVER_PROMO_CODES_SUCCESS), `${apiBaseUrl}/api/v1/summits/${summitId}/promo-codes/all/discover`, // Discovery is non-blocking - errors silently ignored.
469
+ // Auth errors will surface on the next user-initiated action.
470
+ null)({
471
+ access_token: accessToken
472
+ })(dispatch);
473
+ } catch (e) {
474
+ console.log(e);
475
+ return null;
476
+ }
477
+ };
478
+ /*********************************************************************************/
479
+
429
480
  /* TICKETS */
430
481
 
431
482
  /*********************************************************************************/
432
483
  // api/v1/summits/{id}/ticket-types/allowed
433
484
  // api/v1/summits/{id}/tax-types
434
485
 
435
-
436
486
  const getTicketTypesAndTaxes = summitId => async dispatch => {
437
487
  return Promise.all([dispatch(getTicketTypes(summitId)), dispatch(getTaxesTypes(summitId))]).then(values => {
438
488
  return values;
@@ -504,22 +554,23 @@ const getTaxesTypes = summitId => async (dispatch, getState, {
504
554
  }
505
555
  };
506
556
 
507
- const applyPromoCode = currentPromoCode => (dispatch, getState) => {
508
- try {
509
- const {
510
- registrationLiteState: {
511
- settings: {
512
- summitId
513
- }
557
+ const applyPromoCode = currentPromoCode => async (dispatch, getState) => {
558
+ const {
559
+ registrationLiteState: {
560
+ settings: {
561
+ summitId
514
562
  }
515
- } = getState(); // set the current promo code and get ticket types again
563
+ }
564
+ } = getState();
516
565
 
566
+ try {
517
567
  dispatch((0,actions_namespaceObject.createAction)(SET_CURRENT_PROMO_CODE)({
518
568
  currentPromoCode
519
569
  }));
520
- dispatch(getTicketTypes(summitId));
570
+ await dispatch(getTicketTypes(summitId));
521
571
  } catch (e) {
522
- return Promise.reject(e);
572
+ dispatch((0,actions_namespaceObject.createAction)(CLEAR_CURRENT_PROMO_CODE)({}));
573
+ throw e;
523
574
  }
524
575
  };
525
576
  const removePromoCode = () => (dispatch, getState) => {
@@ -539,9 +590,13 @@ const removePromoCode = () => (dispatch, getState) => {
539
590
  return Promise.reject(e);
540
591
  }
541
592
  };
542
- const validatePromoCode = (ticketData, {
543
- onError
544
- }) => async (dispatch, getState, {
593
+ /**
594
+ * Validates promo code for a specific ticket selection.
595
+ * Stores allows_to_reassign in Redux state.
596
+ * Returns response or throws error - caller handles UI concerns.
597
+ */
598
+
599
+ const validatePromoCode = ticketData => async (dispatch, getState, {
545
600
  apiBaseUrl,
546
601
  getAccessToken
547
602
  }) => {
@@ -552,61 +607,24 @@ const validatePromoCode = (ticketData, {
552
607
  },
553
608
  promoCode: currentPromoCode
554
609
  }
555
- } = getState();
556
- const {
557
- promoCode: formPromoCode
558
- } = ticketData;
559
-
560
- if (formPromoCode && !currentPromoCode) {
561
- const defaultMessage = `You entered a promo code but it hasn't been applied. Make sure to click the 'Apply' button or remove it before continuing.`;
562
- const notAppliedCodeError = {
563
- body: {
564
- errors: [defaultMessage]
565
- }
566
- };
567
- return onError(null, notAppliedCodeError);
568
- }
569
-
570
- if (summitId && currentPromoCode) {
571
- dispatch(startWidgetLoading());
572
- const {
573
- ticketQuantity,
574
- id,
575
- sub_type
576
- } = ticketData;
577
- const access_token = await getAccessToken();
578
- let apiUrl = external_urijs_default()(`${apiBaseUrl}/api/v1/summits/${summitId}/promo-codes/${currentPromoCode}/apply`);
579
- apiUrl.addQuery('access_token', access_token);
580
- apiUrl.addQuery('filter[]', `ticket_type_id==${id}`);
581
- apiUrl.addQuery('filter[]', `ticket_type_qty==${ticketQuantity}`);
582
- apiUrl.addQuery('filter[]', `ticket_type_subtype==${sub_type}`);
583
-
584
- const errorHandler = (err, res) => (dispatch, state) => {
585
- if (res && res.statusCode === 404 && onError) return onError(err, res);
586
- if (res && res.statusCode === 412 && onError) return onError(err, res);
587
- if (res && res.statusCode === 429 && onError) return onError(err, res);
588
-
589
- if (res && res.statusCode === 500) {
590
- const defaultMessage = 'Server Error';
591
- const msg = res?.body?.message || defaultMessage;
592
- external_sweetalert2_default().fire("Server Error", msg, "error");
593
- return;
594
- }
610
+ } = getState(); // Nothing to validate if no promo code applied
595
611
 
596
- return (0,actions_namespaceObject.authErrorHandler)(err, res)(dispatch, state);
597
- };
598
-
599
- return (0,actions_namespaceObject.getRequest)(null, (0,actions_namespaceObject.createAction)(VALIDATE_PROMO_CODE), `${apiUrl}`, errorHandler)({})(dispatch).then(res => {
600
- dispatch(changeStep(constants.STEP_PERSONAL_INFO));
601
- dispatch(stopWidgetLoading());
602
- return res;
603
- }).catch(error => {
604
- dispatch(stopWidgetLoading());
605
- return Promise.reject(error);
606
- });
612
+ if (!summitId || !currentPromoCode) {
613
+ return null;
607
614
  }
608
615
 
609
- return dispatch(changeStep(constants.STEP_PERSONAL_INFO));
616
+ const {
617
+ ticketQuantity = 1,
618
+ id,
619
+ sub_type
620
+ } = ticketData;
621
+ const access_token = await getAccessToken();
622
+ let apiUrl = external_urijs_default()(`${apiBaseUrl}/api/v1/summits/${summitId}/promo-codes/${currentPromoCode}/apply`);
623
+ apiUrl.addQuery('access_token', access_token);
624
+ apiUrl.addQuery('filter[]', `ticket_type_id==${id}`);
625
+ apiUrl.addQuery('filter[]', `ticket_type_qty==${ticketQuantity}`);
626
+ apiUrl.addQuery('filter[]', `ticket_type_subtype==${sub_type}`);
627
+ return (0,actions_namespaceObject.getRequest)((0,actions_namespaceObject.createAction)(VALIDATE_PROMO_CODE), (0,actions_namespaceObject.createAction)(VALIDATE_PROMO_CODE_SUCCESS), `${apiUrl}`, promoCodeErrorHandler)({})(dispatch);
610
628
  };
611
629
  const reserveTicket = ({
612
630
  provider,
@@ -1326,7 +1344,7 @@ LoginComponent.defaultProps = {
1326
1344
 
1327
1345
  /***/ }),
1328
1346
 
1329
- /***/ 558:
1347
+ /***/ 95:
1330
1348
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1331
1349
 
1332
1350
  "use strict";
@@ -1355,6 +1373,256 @@ const external_react_use_namespaceObject = require("react-use");
1355
1373
  const constants_namespaceObject = require("openstack-uicore-foundation/lib/security/constants");
1356
1374
  // EXTERNAL MODULE: ./src/actions.js + 5 modules
1357
1375
  var actions = __webpack_require__(595);
1376
+ ;// CONCATENATED MODULE: external "i18n-react"
1377
+ const external_i18n_react_namespaceObject = require("i18n-react");
1378
+ var external_i18n_react_default = /*#__PURE__*/__webpack_require__.n(external_i18n_react_namespaceObject);
1379
+ // EXTERNAL MODULE: ./src/utils/constants.js
1380
+ var constants = __webpack_require__(243);
1381
+ ;// CONCATENATED MODULE: ./src/hooks/usePromoCode.js
1382
+
1383
+
1384
+
1385
+
1386
+ const usePromoCode = ({
1387
+ // Redux state
1388
+ discoveredPromoCodes,
1389
+ promoCode,
1390
+ promoCodeVerified,
1391
+ promoCodeValidating,
1392
+ // Redux dispatchers
1393
+ applyPromoCode,
1394
+ removePromoCode,
1395
+ validatePromoCode,
1396
+ // Form integration
1397
+ ticketDataLoaded = false,
1398
+ hasTickets = false,
1399
+ setFormPromoCode
1400
+ }) => {
1401
+ // Per-session lock: once the user removes (or auto-apply fails for) a
1402
+ // discovered code, don't re-apply it on this widget mount. Never reset.
1403
+ const [userRemovedAutoApply, setUserRemovedAutoApply] = (0,external_react_.useState)(false);
1404
+ const [isAutoApplied, setIsAutoApplied] = (0,external_react_.useState)(false);
1405
+ const [suggestionActive, setSuggestionActive] = (0,external_react_.useState)(false);
1406
+ const [suggestionDismissed, setSuggestionDismissed] = (0,external_react_.useState)(false); // Error written by handleValidationError (API) or the form (unapplied-code warning).
1407
+ // The user-facing `validationError` is computed below by merging this with the
1408
+ // status-derived INVALID message.
1409
+
1410
+ const [manualError, setManualError] = (0,external_react_.useState)(null);
1411
+ const [applyingCode, setApplyingCode] = (0,external_react_.useState)(false); // Pick first auto_apply code, or first code if none has auto_apply
1412
+
1413
+ const discoveredPromoCode = (0,external_react_.useMemo)(() => {
1414
+ if (!discoveredPromoCodes?.length) return null;
1415
+ return discoveredPromoCodes.find(c => c.auto_apply) || discoveredPromoCodes[0];
1416
+ }, [discoveredPromoCodes]);
1417
+ const isApplied = !!promoCode;
1418
+ const isDiscoveredCode = isApplied && discoveredPromoCode?.code === promoCode; // --- Status ---
1419
+
1420
+ const status = (0,external_react_.useMemo)(() => {
1421
+ if (isApplied) {
1422
+ if (promoCodeValidating) return constants.PROMO_STATUS.VALIDATING;
1423
+ if (promoCodeVerified === true) return constants.PROMO_STATUS.VALID;
1424
+ if (promoCodeVerified === false) return constants.PROMO_STATUS.INVALID; // Applied but no tickets returned and not currently applying: code is invalid
1425
+
1426
+ if (!applyingCode && ticketDataLoaded && !hasTickets) return constants.PROMO_STATUS.INVALID;
1427
+ return constants.PROMO_STATUS.APPLYING;
1428
+ }
1429
+
1430
+ if (suggestionActive && !suggestionDismissed) return constants.PROMO_STATUS.SUGGESTED;
1431
+ return constants.PROMO_STATUS.IDLE;
1432
+ }, [isApplied, promoCodeVerified, promoCodeValidating, suggestionActive, suggestionDismissed, applyingCode, ticketDataLoaded, hasTickets]); // Hook's own validation error. Composed from the in-flight API error (if any)
1433
+ // and the status-derived "invalid code" message when status is INVALID.
1434
+ // Consumers may layer their own warning on top before display.
1435
+
1436
+ const validationError = manualError ?? (status === constants.PROMO_STATUS.INVALID ? external_i18n_react_default().translate('promo_code.invalid_code') : null); // --- Derived values ---
1437
+
1438
+ const suggestedCode = discoveredPromoCode?.code || null;
1439
+ const activeDiscoveredCode = status === constants.PROMO_STATUS.VALID && isDiscoveredCode ? discoveredPromoCode : null;
1440
+ const perAccountLimit = activeDiscoveredCode?.quantity_per_account > 0 ? activeDiscoveredCode.remaining_quantity_per_account : null; // Tightest promo-code-level quantity cap for the stepper (discovered codes only).
1441
+ // Both cap sources use `!= null` so a value of 0 (sold-out / no remaining) caps the
1442
+ // stepper at 0 instead of being silently ignored.
1443
+
1444
+ const maxQuantityFromPromo = (0,external_react_.useMemo)(() => {
1445
+ if (!activeDiscoveredCode) return null;
1446
+ const caps = [];
1447
+ if (activeDiscoveredCode.remaining_quantity_per_account != null) caps.push(activeDiscoveredCode.remaining_quantity_per_account);
1448
+ if (activeDiscoveredCode.quantity_available != null) caps.push(activeDiscoveredCode.quantity_available);
1449
+ return caps.length > 0 ? Math.min(...caps) : null;
1450
+ }, [activeDiscoveredCode]); // True when the user can safely advance from the ticket step
1451
+ // (no in-flight promo apply/validate and no INVALID state to block on).
1452
+
1453
+ const isReady = status === constants.PROMO_STATUS.IDLE || status === constants.PROMO_STATUS.SUGGESTED || status === constants.PROMO_STATUS.VALID; // --- Discovery: ticket qualification ---
1454
+
1455
+ const isCodeValidForTicket = (0,external_react_.useCallback)(ticket => {
1456
+ if (!discoveredPromoCode || !ticket) return false;
1457
+ const allowed = discoveredPromoCode.allowed_ticket_types || [];
1458
+ if (allowed.length === 0) return true;
1459
+ return allowed.some(tt => (typeof tt === 'object' ? tt.id : tt) === ticket.id);
1460
+ }, [discoveredPromoCode]); // --- Helpers ---
1461
+
1462
+ const handleValidationError = (0,external_react_.useCallback)(e => {
1463
+ if (e?.res?.body) {
1464
+ const errors = e.res.body.errors || [e.res.body.message || external_i18n_react_default().translate('promo_code.validation_error')];
1465
+ const first = errors[0];
1466
+ const firstStr = typeof first === 'string' ? first : first?.message ?? String(first);
1467
+ const msg = /is not a valid code/i.test(firstStr) ? external_i18n_react_default().translate('promo_code.invalid_code') : firstStr;
1468
+ setManualError(msg);
1469
+ } else {
1470
+ setManualError(external_i18n_react_default().translate('promo_code.validation_error'));
1471
+ }
1472
+ }, []); // --- Actions ---
1473
+
1474
+ const onRevalidate = (0,external_react_.useCallback)(async (ticket, quantity) => {
1475
+ setManualError(null);
1476
+
1477
+ try {
1478
+ await validatePromoCode({
1479
+ id: ticket.id,
1480
+ ticketQuantity: quantity,
1481
+ sub_type: ticket.sub_type
1482
+ });
1483
+ return true;
1484
+ } catch (e) {
1485
+ handleValidationError(e);
1486
+ return false;
1487
+ }
1488
+ }, [validatePromoCode, handleValidationError]); // Shared auto-apply flow. Caller is responsible for the gating conditions
1489
+ // (auto_apply / single code / not already applied / not user-removed) so each
1490
+ // call site keeps its own trigger logic. Returns true if the code was applied
1491
+ // and (if a ticket was passed) successfully revalidated.
1492
+ //
1493
+ // Note on concurrency: the two call sites (early-auto-apply effect and
1494
+ // onTicketSelected's auto-apply branch) operate on disjoint states by design
1495
+ // (the effect requires `!hasTickets`, the branch requires a selected ticket),
1496
+ // so a true concurrent invocation is unreachable in practice. If a future
1497
+ // change makes that overlap possible, gate this body with a ref-tracked
1498
+ // in-flight flag rather than `applyingCode` (which is captured stale here).
1499
+
1500
+ const tryAutoApply = (0,external_react_.useCallback)(async ticket => {
1501
+ setIsAutoApplied(true);
1502
+ setApplyingCode(true);
1503
+
1504
+ try {
1505
+ await applyPromoCode(discoveredPromoCode.code);
1506
+
1507
+ if (ticket) {
1508
+ const valid = await onRevalidate(ticket, 1);
1509
+
1510
+ if (!valid) {
1511
+ setIsAutoApplied(false);
1512
+ return false;
1513
+ }
1514
+ }
1515
+
1516
+ return true;
1517
+ } catch (e) {
1518
+ setIsAutoApplied(false);
1519
+ handleValidationError(e);
1520
+ return false;
1521
+ } finally {
1522
+ setApplyingCode(false);
1523
+ }
1524
+ }, [discoveredPromoCode, applyPromoCode, onRevalidate, handleValidationError]);
1525
+ const onTicketSelected = (0,external_react_.useCallback)(async ticket => {
1526
+ const qualifies = discoveredPromoCode && isCodeValidForTicket(ticket);
1527
+ setSuggestionActive(qualifies);
1528
+ setSuggestionDismissed(false);
1529
+ setManualError(null); // Manual (non-discovered) code is applied: re-validate for new ticket
1530
+
1531
+ if (isApplied && !isDiscoveredCode) {
1532
+ await onRevalidate(ticket, 1);
1533
+ return;
1534
+ }
1535
+
1536
+ if (!discoveredPromoCode) return; // Discovered code is currently applied
1537
+
1538
+ if (isDiscoveredCode) {
1539
+ if (!qualifies) {
1540
+ setIsAutoApplied(false);
1541
+ removePromoCode();
1542
+ } else {
1543
+ const valid = await onRevalidate(ticket, 1);
1544
+ if (!valid) setIsAutoApplied(false);
1545
+ }
1546
+
1547
+ return;
1548
+ } // No code applied, ticket qualifies, auto-apply configured, single code only
1549
+
1550
+
1551
+ if (!isApplied && qualifies && discoveredPromoCode.auto_apply && !userRemovedAutoApply && discoveredPromoCodes.length === 1) {
1552
+ await tryAutoApply(ticket);
1553
+ }
1554
+ }, [discoveredPromoCode, isApplied, isDiscoveredCode, userRemovedAutoApply, discoveredPromoCodes, isCodeValidForTicket, removePromoCode, onRevalidate, tryAutoApply]); // Early auto-apply: when no tickets are available and a single auto_apply code
1555
+ // was discovered, apply it so the API returns WithPromoCode ticket types.
1556
+ // On failure, mark as removed to prevent re-fire loops.
1557
+
1558
+ (0,external_react_.useEffect)(() => {
1559
+ if (userRemovedAutoApply || isApplied) return;
1560
+ if (!ticketDataLoaded || hasTickets) return;
1561
+ if (!discoveredPromoCode?.auto_apply) return;
1562
+ if (discoveredPromoCodes.length !== 1) return;
1563
+ tryAutoApply(null).then(success => {
1564
+ if (!success) setUserRemovedAutoApply(true);
1565
+ });
1566
+ }, [userRemovedAutoApply, ticketDataLoaded, hasTickets, discoveredPromoCode, discoveredPromoCodes, isApplied, tryAutoApply]);
1567
+ const onApply = (0,external_react_.useCallback)(async (code, ticket, quantity) => {
1568
+ setManualError(null);
1569
+ setApplyingCode(true);
1570
+
1571
+ try {
1572
+ await applyPromoCode(code);
1573
+ } catch (e) {
1574
+ handleValidationError(e);
1575
+ setApplyingCode(false);
1576
+ return;
1577
+ }
1578
+
1579
+ if (ticket) {
1580
+ await onRevalidate(ticket, quantity);
1581
+ }
1582
+
1583
+ setApplyingCode(false);
1584
+ }, [applyPromoCode, onRevalidate, handleValidationError]);
1585
+ const onRemove = (0,external_react_.useCallback)(() => {
1586
+ if (isAutoApplied || isDiscoveredCode) setUserRemovedAutoApply(true);
1587
+ setIsAutoApplied(false);
1588
+ setManualError(null);
1589
+ setSuggestionDismissed(false);
1590
+ if (discoveredPromoCode) setSuggestionActive(true);
1591
+ setFormPromoCode('');
1592
+ removePromoCode();
1593
+ }, [isAutoApplied, isDiscoveredCode, discoveredPromoCode, removePromoCode, setFormPromoCode]);
1594
+ const onInputChange = (0,external_react_.useCallback)(value => {
1595
+ setManualError(null);
1596
+ setSuggestionDismissed(value !== discoveredPromoCode?.code);
1597
+ setFormPromoCode(value);
1598
+ }, [discoveredPromoCode, setFormPromoCode]);
1599
+ return {
1600
+ state: {
1601
+ // Status (what's happening with the applied/suggested code)
1602
+ status,
1603
+ isReady,
1604
+ validationError,
1605
+ // Applied code origin
1606
+ isDiscoveredCode,
1607
+ isAutoApplied,
1608
+ // Discovery / suggestion
1609
+ suggestedCode,
1610
+ // Quantity caps from the active discovered code
1611
+ maxQuantityFromPromo,
1612
+ perAccountLimit
1613
+ },
1614
+ actions: {
1615
+ // Ordered by lifecycle: input → apply → ticket change → revalidate → remove
1616
+ onInputChange,
1617
+ onApply,
1618
+ onTicketSelected,
1619
+ onRevalidate,
1620
+ onRemove
1621
+ }
1622
+ };
1623
+ };
1624
+
1625
+ /* harmony default export */ const hooks_usePromoCode = (usePromoCode);
1358
1626
  ;// CONCATENATED MODULE: external "openstack-uicore-foundation/lib/components/ajaxloader"
1359
1627
  const ajaxloader_namespaceObject = require("openstack-uicore-foundation/lib/components/ajaxloader");
1360
1628
  var ajaxloader_default = /*#__PURE__*/__webpack_require__.n(ajaxloader_namespaceObject);
@@ -1376,8 +1644,6 @@ const methods_namespaceObject = require("openstack-uicore-foundation/lib/utils/m
1376
1644
  ;// CONCATENATED MODULE: ./src/components/lawpay-form/index.module.scss
1377
1645
  // extracted by mini-css-extract-plugin
1378
1646
  /* harmony default export */ const lawpay_form_index_module = ({"form":"form___zXb7s","fieldWrapper":"fieldWrapper___G4Wqw","inputWrapper":"inputWrapper___Yz5zB","fieldRow":"fieldRow___NfZdJ","addressField":"addressField___vmAQh","lawpayWrapper":"lawpayWrapper___hpUBf","dateWrapper":"dateWrapper___XDfqs","dropdown":"dropdown___l3_bk","fieldError":"fieldError___Igq3U"});
1379
- // EXTERNAL MODULE: ./src/utils/constants.js
1380
- var constants = __webpack_require__(243);
1381
1647
  ;// CONCATENATED MODULE: ./src/components/lawpay-form/index.js
1382
1648
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
1383
1649
 
@@ -2122,7 +2388,7 @@ var company_input_v2_default = /*#__PURE__*/__webpack_require__.n(company_input_
2122
2388
  var helpers = __webpack_require__(499);
2123
2389
  ;// CONCATENATED MODULE: ./src/components/personal-information/index.module.scss
2124
2390
  // extracted by mini-css-extract-plugin
2125
- /* harmony default export */ const personal_information_index_module = ({"title":"title___ECoNz","form":"form___lDFka","fieldWrapper":"fieldWrapper___Mi_nL","fieldWrapperRadio":"fieldWrapperRadio___x18VG","inputWrapper":"inputWrapper___PEQFR","readOnly":"readOnly___WRazF","fieldError":"fieldError___ksJVe","moreInfo":"moreInfo___cQYdZ","moreInfoTooltip":"moreInfoTooltip___lslgT","ticketQuantityNotice":"ticketQuantityNotice___L6gis","formErrors":"formErrors___dQQMe"});
2391
+ /* harmony default export */ const personal_information_index_module = ({"title":"title___ECoNz","form":"form___lDFka","fieldWrapper":"fieldWrapper___Mi_nL","fieldWrapperRadio":"fieldWrapperRadio___x18VG","form-check-label":"form-check-label___MgGSC","inputWrapper":"inputWrapper___PEQFR","readOnly":"readOnly___WRazF","fieldError":"fieldError___ksJVe","moreInfo":"moreInfo___cQYdZ","moreInfoTooltip":"moreInfoTooltip___lslgT","ticketQuantityNotice":"ticketQuantityNotice___L6gis","formErrors":"formErrors___dQQMe"});
2126
2392
  ;// CONCATENATED MODULE: ./src/components/personal-information/index.js
2127
2393
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2128
2394
 
@@ -2163,20 +2429,27 @@ const PersonalInfoComponent = ({
2163
2429
  summitId,
2164
2430
  handleCompanyError,
2165
2431
  formValues,
2166
- formErrors = {},
2432
+ formErrors = [],
2167
2433
  invitation,
2168
2434
  showCompanyInput = true,
2169
2435
  companyDDLPlaceholder,
2170
2436
  showCompanyInputDefaultOptions,
2171
- companyDDLOptions2Show
2437
+ companyDDLOptions2Show,
2438
+ promoCodeAllowsReassign = true
2172
2439
  }) => {
2173
2440
  const initialFirstName = userProfile.given_name || (invitation ? invitation.first_name : '');
2174
2441
  const initialLastName = userProfile.family_name || (invitation ? invitation.last_name : '');
2175
2442
  const [ticketOwnerOption, setTicketOwnerOption] = (0,external_react_.useState)('');
2176
- const [ticketOwnerError, setTicketOwnerError] = (0,external_react_.useState)(false); // if there's only one ticket on the order and there is no invitation available, display the radio button to assign the ticket
2443
+ const [ticketOwnerError, setTicketOwnerError] = (0,external_react_.useState)(false); // if there's only one ticket on the order and there is no invitation available, handle ticket assignment
2444
+
2445
+ const isSingleTicketOrder = formValues.ticketQuantity === 1 && !invitation && !(0,utils/* isPrePaidTicketType */.B6)(formValues.ticketType); // check if reassignment is allowed by both promo code AND ticket type
2177
2446
 
2178
- const shouldDisplayTicketAssignment = () => formValues.ticketQuantity === 1 && !invitation && !(0,utils/* isPrePaidTicketType */.B6)(formValues.ticketType);
2447
+ const ticketTypeAllowsReassign = formValues.ticketType?.allows_to_reassign !== false;
2448
+ const canReassign = promoCodeAllowsReassign && ticketTypeAllowsReassign; // show radio options only if reassignment is allowed by both sources
2179
2449
 
2450
+ const shouldDisplayTicketAssignment = isSingleTicketOrder && canReassign; // show notice when ticket is non-transferable (either promo code or ticket type disallows)
2451
+
2452
+ const isNonTransferable = isSingleTicketOrder && !canReassign;
2180
2453
  const radioListOptions = [{
2181
2454
  label: "Myself",
2182
2455
  value: constants.TICKET_OWNER_MYSELF
@@ -2219,7 +2492,7 @@ const PersonalInfoComponent = ({
2219
2492
  email: reservation.owner_email ? reservation.owner_email : personalInfo.email,
2220
2493
  company: {
2221
2494
  id: null,
2222
- name: reservation.owner_company ? reservation.owner_company : personalInfo.company
2495
+ name: reservation.owner_company || personalInfo.company?.name || ''
2223
2496
  }
2224
2497
  });
2225
2498
  }
@@ -2235,12 +2508,21 @@ const PersonalInfoComponent = ({
2235
2508
  };
2236
2509
 
2237
2510
  const onSubmit = data => {
2238
- if (!personalInfo.company.name && showCompanyInput) {
2511
+ if (!personalInfo.company?.name && showCompanyInput) {
2239
2512
  setCompanyError(true);
2240
2513
  return;
2241
2514
  }
2242
2515
 
2243
- if (shouldDisplayTicketAssignment()) {
2516
+ if (isNonTransferable) {
2517
+ // auto-assign to purchaser when ticket is non-transferable
2518
+ data = personal_information_objectSpread(personal_information_objectSpread({}, data), {}, {
2519
+ attendee: {
2520
+ firstName: data.firstName,
2521
+ lastName: data.lastName,
2522
+ email: data.email
2523
+ }
2524
+ });
2525
+ } else if (shouldDisplayTicketAssignment) {
2244
2526
  if (!ticketOwnerOption) {
2245
2527
  setTicketOwnerError(true);
2246
2528
  return;
@@ -2337,7 +2619,7 @@ const PersonalInfoComponent = ({
2337
2619
  className: personal_information_index_module.title
2338
2620
  }, /*#__PURE__*/external_react_default().createElement("span", null, "Purchaser Information"), !isActive && /*#__PURE__*/external_react_default().createElement("div", {
2339
2621
  "data-testid": "personal-info"
2340
- }, /*#__PURE__*/external_react_default().createElement("span", null, `${personalInfo.firstName} ${personalInfo.lastName}${personalInfo.company.name ? ` - ${personalInfo.company.name}` : ''}`), /*#__PURE__*/external_react_default().createElement("br", null), /*#__PURE__*/external_react_default().createElement("span", null, personalInfo.email))), /*#__PURE__*/external_react_default().createElement(external_react_spring_namespaceObject.animated.div, {
2622
+ }, /*#__PURE__*/external_react_default().createElement("span", null, `${personalInfo.firstName} ${personalInfo.lastName}${personalInfo.company?.name ? ` - ${personalInfo.company.name}` : ''}`), /*#__PURE__*/external_react_default().createElement("br", null), /*#__PURE__*/external_react_default().createElement("span", null, personalInfo.email))), /*#__PURE__*/external_react_default().createElement(external_react_spring_namespaceObject.animated.div, {
2341
2623
  style: personal_information_objectSpread({
2342
2624
  overflow: `${isActive ? '' : 'hidden'}`
2343
2625
  }, toggleAnimation)
@@ -2432,7 +2714,7 @@ const PersonalInfoComponent = ({
2432
2714
  }), companyError && /*#__PURE__*/external_react_default().createElement("div", {
2433
2715
  className: personal_information_index_module.fieldError,
2434
2716
  "data-testid": "company-error"
2435
- }, "This field is required."))), shouldDisplayTicketAssignment() && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement("div", {
2717
+ }, "This field is required."))), shouldDisplayTicketAssignment && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement("div", {
2436
2718
  className: personal_information_index_module.fieldWrapperRadio
2437
2719
  }, /*#__PURE__*/external_react_default().createElement("label", null, "Ticket is for:"), /*#__PURE__*/external_react_default().createElement(components_namespaceObject.RadioList, {
2438
2720
  id: `ticket-self-radio`,
@@ -2500,12 +2782,9 @@ const PersonalInfoComponent = ({
2500
2782
  ;// CONCATENATED MODULE: external "openstack-uicore-foundation/lib/components/raw-html"
2501
2783
  const raw_html_namespaceObject = require("openstack-uicore-foundation/lib/components/raw-html");
2502
2784
  var raw_html_default = /*#__PURE__*/__webpack_require__.n(raw_html_namespaceObject);
2503
- ;// CONCATENATED MODULE: external "i18n-react"
2504
- const external_i18n_react_namespaceObject = require("i18n-react");
2505
- var external_i18n_react_default = /*#__PURE__*/__webpack_require__.n(external_i18n_react_namespaceObject);
2506
2785
  ;// CONCATENATED MODULE: ./src/components/ticket-type/index.module.scss
2507
2786
  // extracted by mini-css-extract-plugin
2508
- /* harmony default export */ const ticket_type_index_module = ({"title":"title___DNZyl","summary":"summary___quWdZ","promoCode":"promoCode___bqTCw","crossOut":"crossOut___QZ7dy","discount":"discount___sEK_Q","promocodeError":"promocodeError___oxk4p","taxes":"taxes___fe8oJ","promo":"promo___F8lPO","form":"form___aoo7w","dropdown":"dropdown____HWg0","quantity":"quantity___SIEQZ","soldOut":"soldOut___Hatfr","moreInfo":"moreInfo___LmwOe","moreInfoTooltip":"moreInfoTooltip___nOBf1","inPersonDisclaimer":"inPersonDisclaimer___PXGTz"});
2787
+ /* harmony default export */ const ticket_type_index_module = ({"title":"title___DNZyl","summary":"summary___quWdZ","promoCode":"promoCode___bqTCw","crossOut":"crossOut___QZ7dy","discount":"discount___sEK_Q","taxes":"taxes___fe8oJ","promo":"promo___F8lPO","form":"form___aoo7w","dropdown":"dropdown____HWg0","quantity":"quantity___SIEQZ","soldOut":"soldOut___Hatfr","moreInfo":"moreInfo___LmwOe","moreInfoTooltip":"moreInfoTooltip___nOBf1","inPersonDisclaimer":"inPersonDisclaimer___PXGTz"});
2509
2788
  ;// CONCATENATED MODULE: external "lodash/isEqual"
2510
2789
  const isEqual_namespaceObject = require("lodash/isEqual");
2511
2790
  var isEqual_default = /*#__PURE__*/__webpack_require__.n(isEqual_namespaceObject);
@@ -2550,7 +2829,7 @@ const TicketDropdownComponent = ({
2550
2829
  (0,external_react_.useEffect)(() => {
2551
2830
  const prevTicketTypes = prevTicketTypesRef.current;
2552
2831
 
2553
- if (!isEqual_default()(ticketTypes, []) && !isEqual_default()(prevTicketTypes, ticketTypes)) {
2832
+ if (!isEqual_default()(prevTicketTypes, ticketTypes)) {
2554
2833
  setCurrentTicketTypes(ticketTypes);
2555
2834
  }
2556
2835
 
@@ -2593,12 +2872,9 @@ const TicketDropdownComponent = ({
2593
2872
  ;// CONCATENATED MODULE: external "react-tooltip"
2594
2873
  const external_react_tooltip_namespaceObject = require("react-tooltip");
2595
2874
  var external_react_tooltip_default = /*#__PURE__*/__webpack_require__.n(external_react_tooltip_namespaceObject);
2596
- // EXTERNAL MODULE: ./src/assets/icon-check-circle.svg
2597
- var icon_check_circle = __webpack_require__(60);
2598
- var icon_check_circle_default = /*#__PURE__*/__webpack_require__.n(icon_check_circle);
2599
2875
  ;// CONCATENATED MODULE: ./src/components/promocode-input/index.module.scss
2600
2876
  // extracted by mini-css-extract-plugin
2601
- /* harmony default export */ const promocode_input_index_module = ({"promoCodeWrapper":"promoCodeWrapper___aw3Zx","promoCodeInput":"promoCodeInput___rDiET","promoCodeActive":"promoCodeActive___j7xnn","codeButtonWrapper":"codeButtonWrapper___jVZh5","noCode":"noCode___YUmVy","appliedCodeIcon":"appliedCodeIcon___pa3B4","moreInfo":"moreInfo___Ru3Rv","moreInfoTooltip":"moreInfoTooltip___eaYWm"});
2877
+ /* harmony default export */ const promocode_input_index_module = ({"promoCodeWrapper":"promoCodeWrapper___aw3Zx","promoCodeInput":"promoCodeInput___rDiET","promoCodeActive":"promoCodeActive___j7xnn","codeButtonWrapper":"codeButtonWrapper___jVZh5","noCode":"noCode___YUmVy","statusIcon":"statusIcon___l1uV0","valid":"valid___pDUq_","invalid":"invalid___UO9dX","spinner":"spinner___SKEJg","spin":"spin___wP5uK","moreInfo":"moreInfo___Ru3Rv","moreInfoTooltip":"moreInfoTooltip___eaYWm"});
2602
2878
  ;// CONCATENATED MODULE: ./src/components/promocode-input/index.js
2603
2879
  /**
2604
2880
  * Copyright 2020 OpenStack Foundation
@@ -2620,56 +2896,101 @@ var icon_check_circle_default = /*#__PURE__*/__webpack_require__.n(icon_check_ci
2620
2896
 
2621
2897
 
2622
2898
  const PromoCodeInput = ({
2623
- applyPromoCode,
2899
+ promoStatus,
2624
2900
  promoCode,
2625
- removePromoCode,
2626
- showMultipleTicketTexts,
2627
- onPromoCodeChange
2901
+ suggestedCode,
2902
+ isAutoApplied,
2903
+ onApply,
2904
+ onRemove,
2905
+ onInputChange,
2906
+ showMultipleTicketTexts
2628
2907
  }) => {
2629
- const [statePromoCode, setStatePromoCode] = (0,external_react_.useState)(promoCode);
2908
+ const [userTypedValue, setUserTypedValue] = (0,external_react_.useState)('');
2909
+ (0,external_react_.useEffect)(() => {
2910
+ if (!promoCode) setUserTypedValue('');
2911
+ }, [promoCode]); // Lock the input + show Remove (instead of Apply) whenever a code is in flight
2912
+ // or has settled (valid or invalid). The user must explicitly Remove to edit again.
2913
+
2914
+ const isLocked = promoStatus === constants.PROMO_STATUS.APPLYING || promoStatus === constants.PROMO_STATUS.VALIDATING || promoStatus === constants.PROMO_STATUS.VALID || promoStatus === constants.PROMO_STATUS.INVALID;
2915
+ const inputValue = (0,external_react_.useMemo)(() => {
2916
+ if (promoCode) return promoCode;
2917
+ if (promoStatus === constants.PROMO_STATUS.SUGGESTED) return suggestedCode || '';
2918
+ return userTypedValue;
2919
+ }, [promoCode, promoStatus, suggestedCode, userTypedValue]);
2920
+ const label = (0,external_react_.useMemo)(() => {
2921
+ switch (promoStatus) {
2922
+ case constants.PROMO_STATUS.VALID:
2923
+ if (isAutoApplied) return external_i18n_react_default().translate('promo_code.auto_applied_label');
2924
+ return external_i18n_react_default().translate('promo_code.applied_label');
2925
+
2926
+ case constants.PROMO_STATUS.APPLYING:
2927
+ case constants.PROMO_STATUS.VALIDATING:
2928
+ if (isAutoApplied) return external_i18n_react_default().translate('promo_code.auto_applied_label');
2929
+ return external_i18n_react_default().translate('promo_code.applying_label');
2930
+
2931
+ case constants.PROMO_STATUS.INVALID:
2932
+ return undefined;
2933
+
2934
+ case constants.PROMO_STATUS.SUGGESTED:
2935
+ return external_i18n_react_default().translate('promo_code.suggestion_label');
2936
+
2937
+ default:
2938
+ return undefined;
2939
+ }
2940
+ }, [promoStatus, isAutoApplied]);
2941
+ const canApply = !isLocked && !!inputValue;
2630
2942
 
2631
- const handlePromoCodeChange = value => {
2632
- onPromoCodeChange(value);
2633
- setStatePromoCode(value);
2943
+ const handleInputChange = value => {
2944
+ setUserTypedValue(value);
2945
+ onInputChange(value);
2634
2946
  };
2635
2947
 
2636
- (0,external_react_.useEffect)(() => {
2637
- if ((0,utils/* isEmptyString */.ke)(promoCode)) handlePromoCodeChange(promoCode);
2638
- }, [promoCode]);
2639
2948
  return /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement("div", {
2640
2949
  className: promocode_input_index_module.promoCodeWrapper
2641
- }, /*#__PURE__*/external_react_default().createElement("span", null, "Do you have a promo code?"), /*#__PURE__*/external_react_default().createElement("div", {
2950
+ }, /*#__PURE__*/external_react_default().createElement("span", {
2951
+ style: {
2952
+ display: 'flex',
2953
+ justifyContent: 'space-between',
2954
+ alignItems: 'center'
2955
+ }
2956
+ }, /*#__PURE__*/external_react_default().createElement("span", null, label || external_i18n_react_default().translate('promo_code.default_label')), showMultipleTicketTexts && /*#__PURE__*/external_react_default().createElement("a", {
2957
+ "data-tip": true,
2958
+ "data-for": "promo-code-info",
2959
+ className: promocode_input_index_module.moreInfo,
2960
+ style: {
2961
+ margin: 0
2962
+ }
2963
+ }, /*#__PURE__*/external_react_default().createElement("i", {
2964
+ className: "glyphicon glyphicon-info-sign",
2965
+ "aria-hidden": "true"
2966
+ }), ` `, "Have multiple promo codes?")), /*#__PURE__*/external_react_default().createElement("div", {
2642
2967
  className: promocode_input_index_module.promoCodeInput
2643
2968
  }, /*#__PURE__*/external_react_default().createElement("input", {
2644
- className: `${promoCode ? promocode_input_index_module.promoCodeActive : ''}`,
2969
+ className: `${isLocked ? promocode_input_index_module.promoCodeActive : ''}`,
2645
2970
  type: "text",
2646
- value: statePromoCode,
2647
- onChange: ev => handlePromoCodeChange(ev.target.value),
2971
+ value: inputValue,
2972
+ onChange: ev => handleInputChange(ev.target.value),
2648
2973
  placeholder: "Enter your promo code",
2649
2974
  onKeyDown: e => {
2650
- if (e.key === "Enter") applyPromoCode(statePromoCode);
2975
+ if (e.key === "Enter" && canApply) onApply(inputValue);
2651
2976
  },
2652
- readOnly: !(0,utils/* isEmptyString */.ke)(promoCode)
2653
- }), promoCode && /*#__PURE__*/external_react_default().createElement("img", {
2654
- src: (icon_check_circle_default()),
2655
- className: promocode_input_index_module.appliedCodeIcon
2656
- }), /*#__PURE__*/external_react_default().createElement("div", {
2657
- className: `${promocode_input_index_module.codeButtonWrapper} ${statePromoCode ? '' : promocode_input_index_module.noCode}`
2658
- }, promoCode !== '' ? /*#__PURE__*/external_react_default().createElement("button", {
2659
- onClick: () => removePromoCode()
2977
+ readOnly: isLocked
2978
+ }), (promoStatus === constants.PROMO_STATUS.VALIDATING || promoStatus === constants.PROMO_STATUS.APPLYING) && /*#__PURE__*/external_react_default().createElement("span", {
2979
+ className: `${promocode_input_index_module.statusIcon} ${promocode_input_index_module.spinner}`
2980
+ }), promoStatus === constants.PROMO_STATUS.VALID && /*#__PURE__*/external_react_default().createElement("span", {
2981
+ className: `${promocode_input_index_module.statusIcon} ${promocode_input_index_module.valid}`
2982
+ }, "\u2713"), promoStatus === constants.PROMO_STATUS.INVALID && /*#__PURE__*/external_react_default().createElement("span", {
2983
+ className: `${promocode_input_index_module.statusIcon} ${promocode_input_index_module.invalid}`
2984
+ }, "\u2715"), /*#__PURE__*/external_react_default().createElement("div", {
2985
+ className: `${promocode_input_index_module.codeButtonWrapper} ${inputValue ? '' : promocode_input_index_module.noCode}`
2986
+ }, isLocked ? /*#__PURE__*/external_react_default().createElement("button", {
2987
+ onClick: onRemove
2660
2988
  }, "Remove") : /*#__PURE__*/external_react_default().createElement("button", {
2661
- disabled: !statePromoCode,
2662
- onClick: () => applyPromoCode(statePromoCode)
2663
- }, "Apply"))), showMultipleTicketTexts && /*#__PURE__*/external_react_default().createElement("div", {
2664
- className: promocode_input_index_module.moreInfo
2665
- }, /*#__PURE__*/external_react_default().createElement("a", {
2666
- "data-tip": true,
2667
- "data-for": "promo-code-info"
2668
- }, /*#__PURE__*/external_react_default().createElement("i", {
2669
- className: "glyphicon glyphicon-info-sign",
2670
- "aria-hidden": "true"
2671
- }), ` `, "Have multiple promo codes?"))), /*#__PURE__*/external_react_default().createElement((external_react_tooltip_default()), {
2989
+ disabled: !canApply,
2990
+ onClick: () => onApply(inputValue)
2991
+ }, "Apply")))), /*#__PURE__*/external_react_default().createElement((external_react_tooltip_default()), {
2672
2992
  id: "promo-code-info",
2993
+ place: "bottom",
2673
2994
  overridePosition: utils/* avoidTooltipOverflow */.kb
2674
2995
  }, /*#__PURE__*/external_react_default().createElement("div", {
2675
2996
  className: promocode_input_index_module.moreInfoTooltip
@@ -2677,6 +2998,32 @@ const PromoCodeInput = ({
2677
2998
  };
2678
2999
 
2679
3000
  /* harmony default export */ const promocode_input = (PromoCodeInput);
3001
+ ;// CONCATENATED MODULE: ./src/components/ticket-notice/index.module.scss
3002
+ // extracted by mini-css-extract-plugin
3003
+ /* harmony default export */ const ticket_notice_index_module = ({"notice":"notice____Pa2z","error":"error___WzZms","info":"info___WFfzs","icon":"icon___YWZms"});
3004
+ ;// CONCATENATED MODULE: ./src/components/ticket-notice/index.js
3005
+
3006
+ // `message` is either a single string or an array of strings. Arrays render
3007
+ // stacked consecutively inside the same notice box. Returns null when the
3008
+ // message is empty/unset so callers can pass conditional arrays without an
3009
+ // outer guard.
3010
+
3011
+ const TicketNotice = ({
3012
+ message,
3013
+ variant = 'info'
3014
+ }) => {
3015
+ const items = Array.isArray(message) ? message : message ? [message] : [];
3016
+ if (items.length === 0) return null;
3017
+ return /*#__PURE__*/external_react_default().createElement("div", {
3018
+ className: `${ticket_notice_index_module.notice} ${ticket_notice_index_module[variant]}`
3019
+ }, variant === 'error' && /*#__PURE__*/external_react_default().createElement("span", {
3020
+ className: ticket_notice_index_module.icon
3021
+ }, "\u26A0"), items.map((m, i) => /*#__PURE__*/external_react_default().createElement("div", {
3022
+ key: i
3023
+ }, m)));
3024
+ };
3025
+
3026
+ /* harmony default export */ const ticket_notice = (TicketNotice);
2680
3027
  ;// CONCATENATED MODULE: ./src/components/ticket-type/index.js
2681
3028
  function ticket_type_ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
2682
3029
 
@@ -2711,6 +3058,7 @@ function ticket_type_defineProperty(obj, key, value) { if (key in obj) { Object.
2711
3058
 
2712
3059
 
2713
3060
 
3061
+
2714
3062
  const TicketTypeComponent = ({
2715
3063
  allowedTicketTypes,
2716
3064
  originalTicketTypes,
@@ -2718,20 +3066,35 @@ const TicketTypeComponent = ({
2718
3066
  taxTypes,
2719
3067
  isActive,
2720
3068
  changeForm,
2721
- formErrors,
2722
3069
  reservation,
2723
3070
  inPersonDisclaimer,
2724
3071
  showMultipleTicketTexts,
2725
3072
  allowPromoCodes,
2726
- applyPromoCode,
2727
- removePromoCode,
3073
+ promo = {},
3074
+ validationError,
2728
3075
  promoCode,
2729
- trackViewItem
3076
+ promoCodeAllowsReassign = true,
3077
+ trackViewItem,
3078
+ noTicketsAvailableMessage
2730
3079
  }) => {
3080
+ const {
3081
+ state: promoState = {},
3082
+ actions: promoActions = {}
3083
+ } = promo;
2731
3084
  const [ticket, setTicket] = (0,external_react_.useState)(null);
2732
3085
  const [quantity, setQuantity] = (0,external_react_.useState)(1);
2733
3086
  const minQuantity = 1;
2734
- const maxQuantity = (0,helpers/* getTicketMaxQuantity */.UE)(ticket);
3087
+ const maxQuantity = (0,helpers/* getTicketMaxQuantity */.UE)(ticket, promoState.maxQuantityFromPromo); // Clamp quantity when max changes (e.g. per-account limit kicks in after auto-apply).
3088
+ // If the cap drops below minQuantity (e.g. cap of 0), use the cap directly rather
3089
+ // than flooring at minQuantity, otherwise quantity would end up above the cap.
3090
+
3091
+ (0,external_react_.useEffect)(() => {
3092
+ if (!ticket) return;
3093
+
3094
+ if (quantity > maxQuantity) {
3095
+ setQuantity(maxQuantity < minQuantity ? 0 : maxQuantity);
3096
+ }
3097
+ }, [maxQuantity, quantity, ticket]);
2735
3098
  const [ref, {
2736
3099
  height
2737
3100
  }] = (0,external_react_use_namespaceObject.useMeasure)();
@@ -2755,17 +3118,25 @@ const TicketTypeComponent = ({
2755
3118
  }
2756
3119
  }, []);
2757
3120
  (0,external_react_.useEffect)(() => {
3121
+ const ticketSelectionValid = !!ticket && quantity >= minQuantity && quantity <= maxQuantity;
2758
3122
  changeForm({
2759
3123
  ticketType: ticket,
2760
- ticketQuantity: quantity
3124
+ ticketQuantity: quantity,
3125
+ ticketSelectionValid
2761
3126
  });
2762
- }, [ticket, quantity]);
3127
+ }, [ticket, quantity, maxQuantity]);
2763
3128
  (0,external_react_.useEffect)(() => {
2764
- // if the promo code had changed ( set or not set)
2765
- // try to find the updated ticket from the original ticket types collection from api
2766
- // and update the current ticket that exist on component state
2767
- // bc a discount could be applied to the current selected ticket type
2768
- if (!ticket) return;
3129
+ // When promo code changes, the API returns updated ticket types with/without discount.
3130
+ // Sync the selected ticket with the refreshed data.
3131
+ if (!ticket) {
3132
+ // Auto-select if only one ticket type available after promo code applied
3133
+ if (promoCode && originalTicketTypes.length === 1) {
3134
+ handleTicketChange(originalTicketTypes[0]);
3135
+ }
3136
+
3137
+ return;
3138
+ }
3139
+
2769
3140
  const updatedCurrentTicket = originalTicketTypes.find(t => t?.id === ticket.id);
2770
3141
 
2771
3142
  if (updatedCurrentTicket) {
@@ -2773,35 +3144,62 @@ const TicketTypeComponent = ({
2773
3144
  ticketType: updatedCurrentTicket
2774
3145
  });
2775
3146
  setTicket(updatedCurrentTicket);
3147
+ } else {
3148
+ setTicket(null);
3149
+ setQuantity(minQuantity);
2776
3150
  }
2777
-
2778
- if (!promoCode) changeForm({
2779
- promoCode: ''
2780
- });
2781
3151
  }, [promoCode, originalTicketTypes]);
2782
- const isPrePaidReservation = (0,external_react_.useMemo)(() => reservation ? (0,utils/* isPrePaidOrder */.xm)(reservation) : false, [reservation]);
3152
+ const showTicketSelector = allowedTicketTypes.length > 0;
3153
+ const isPrePaidReservation = (0,external_react_.useMemo)(() => reservation ? (0,utils/* isPrePaidOrder */.xm)(reservation) : false, [reservation]); // check if reassignment is allowed by both promo code AND ticket type
3154
+
3155
+ const ticketTypeAllowsReassign = ticket?.allows_to_reassign !== false;
3156
+ const canReassign = promoCodeAllowsReassign && ticketTypeAllowsReassign; // Per-order cap is interesting only when it's tighter than what inventory
3157
+ // would otherwise allow (i.e. the binding constraint on the stepper).
3158
+
3159
+ const ticketPerOrderLimit = (0,external_react_.useMemo)(() => {
3160
+ if (!ticket) return null;
3161
+ const cap = ticket.max_quantity_per_order;
3162
+ const inventory = (ticket.quantity_2_sell ?? Number.MAX_SAFE_INTEGER) - (ticket.quantity_sold ?? 0);
3163
+ return cap != null && cap > 0 && cap < inventory ? cap : null;
3164
+ }, [ticket]); // Messages composed for the info notice (stacked in display order):
3165
+ // (1) promo per-account cap, (2) ticket-type per-order cap, (3) non-transferable.
3166
+
3167
+ const infoMessage = (0,external_react_.useMemo)(() => {
3168
+ if (!ticket) return [];
3169
+ const lines = [];
3170
+
3171
+ if (promoState.perAccountLimit != null) {
3172
+ lines.push(external_i18n_react_default().translate(promoState.perAccountLimit === 1 ? 'promo_code.per_account_limit_one' : 'promo_code.per_account_limit_other', {
3173
+ limit: promoState.perAccountLimit
3174
+ }));
3175
+ }
2783
3176
 
2784
- const handleTicketChange = t => {
3177
+ if (ticketPerOrderLimit != null) {
3178
+ lines.push(external_i18n_react_default().translate(ticketPerOrderLimit === 1 ? 'ticket_type.max_per_order_one' : 'ticket_type.max_per_order_other', {
3179
+ limit: ticketPerOrderLimit
3180
+ }));
3181
+ }
3182
+
3183
+ if (!canReassign) {
3184
+ lines.push(external_i18n_react_default().translate('promo_code.non_transferable'));
3185
+ }
3186
+
3187
+ return lines;
3188
+ }, [ticket, promoState.perAccountLimit, ticketPerOrderLimit, canReassign]);
3189
+
3190
+ const handleTicketChange = async t => {
2785
3191
  setTicket(t);
2786
3192
  setQuantity(minQuantity);
2787
3193
  trackViewItem(t);
2788
- };
2789
-
2790
- const handlePromoCodeChange = code => {
2791
- changeForm({
2792
- promoCode: code
2793
- });
3194
+ await promoActions.onTicketSelected(t);
2794
3195
  };
2795
3196
 
2796
3197
  const incrementQuantity = () => setQuantity(quantity + 1);
2797
3198
 
2798
3199
  const decrementQuantity = () => setQuantity(quantity - 1);
2799
3200
 
2800
- const promoCodeError = Object.keys(formErrors).length > 0 ? formErrors : null;
2801
-
2802
- const handleRemovePromoCode = () => {
2803
- setTicket(null);
2804
- removePromoCode();
3201
+ const handleApplyPromoCode = async code => {
3202
+ await promoActions.onApply(code, ticket, quantity);
2805
3203
  };
2806
3204
 
2807
3205
  return /*#__PURE__*/external_react_default().createElement("div", {
@@ -2810,9 +3208,26 @@ const TicketTypeComponent = ({
2810
3208
  className: ticket_type_index_module.innerWrapper
2811
3209
  }, /*#__PURE__*/external_react_default().createElement("div", {
2812
3210
  className: ticket_type_index_module.title
2813
- }, /*#__PURE__*/external_react_default().createElement("span", null, "Ticket"), /*#__PURE__*/external_react_default().createElement("div", {
3211
+ }, /*#__PURE__*/external_react_default().createElement("span", {
3212
+ style: isActive ? {
3213
+ display: 'flex',
3214
+ justifyContent: 'space-between',
3215
+ alignItems: 'center',
3216
+ width: '100%'
3217
+ } : {}
3218
+ }, /*#__PURE__*/external_react_default().createElement("span", null, "Ticket"), isActive && showMultipleTicketTexts && /*#__PURE__*/external_react_default().createElement("a", {
3219
+ className: ticket_type_index_module.moreInfo,
3220
+ "data-tip": true,
3221
+ "data-for": "ticket-quantity-info",
3222
+ style: {
3223
+ margin: 0
3224
+ }
3225
+ }, /*#__PURE__*/external_react_default().createElement("i", {
3226
+ className: "glyphicon glyphicon-info-sign",
3227
+ "aria-hidden": "true"
3228
+ }), ` `, "Need multiple ticket types?")), /*#__PURE__*/external_react_default().createElement("div", {
2814
3229
  className: ticket_type_index_module.summary
2815
- }, /*#__PURE__*/external_react_default().createElement("span", null, ticket && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, `${ticket.name} (${quantity}): `, /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, (0,utils/* getTicketCost */.fX)(ticket, quantity)), `${(0,utils/* getTicketTaxes */.h5)(ticket, taxTypes)}`, !isActive && reservation?.discount_amount > 0 && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement("br", null), /*#__PURE__*/external_react_default().createElement("span", {
3230
+ }, /*#__PURE__*/external_react_default().createElement("span", null, !isActive && ticket && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, `${ticket.name} (${quantity}): `, /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, (0,utils/* getTicketCost */.fX)(ticket, quantity)), `${(0,utils/* getTicketTaxes */.h5)(ticket, taxTypes)}`, !isActive && reservation?.discount_amount > 0 && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement("br", null), /*#__PURE__*/external_react_default().createElement("span", {
2816
3231
  className: ticket_type_index_module.promoCode
2817
3232
  }, "Promo code\xA0", /*#__PURE__*/external_react_default().createElement("abbr", {
2818
3233
  title: reservation.promo_code
@@ -2834,13 +3249,13 @@ const TicketTypeComponent = ({
2834
3249
  })} ${ticket.currency}`), /*#__PURE__*/external_react_default().createElement("br", null));
2835
3250
  })), !isActive && reservation && !isPrePaidReservation && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement("br", null), "Total: ", `${(0,helpers/* formatCurrency */.xG)(reservation.amount, {
2836
3251
  currency: ticket.currency
2837
- })} ${ticket.currency}`)), !ticket && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, "No ticket selected")))), /*#__PURE__*/external_react_default().createElement(external_react_spring_namespaceObject.animated.div, {
3252
+ })} ${ticket.currency}`))))), /*#__PURE__*/external_react_default().createElement(external_react_spring_namespaceObject.animated.div, {
2838
3253
  style: ticket_type_objectSpread({
2839
3254
  overflow: 'hidden'
2840
3255
  }, toggleAnimation)
2841
3256
  }, /*#__PURE__*/external_react_default().createElement("div", {
2842
3257
  ref: ref
2843
- }, /*#__PURE__*/external_react_default().createElement("div", {
3258
+ }, showTicketSelector && /*#__PURE__*/external_react_default().createElement("div", {
2844
3259
  className: ticket_type_index_module.form
2845
3260
  }, /*#__PURE__*/external_react_default().createElement("div", {
2846
3261
  className: ticket_type_index_module.dropdown
@@ -2879,28 +3294,31 @@ const TicketTypeComponent = ({
2879
3294
  disabled: maxQuantity === 0 || quantity >= maxQuantity
2880
3295
  }, /*#__PURE__*/external_react_default().createElement("i", {
2881
3296
  className: "fa fa-plus"
2882
- }))))))), allowPromoCodes && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement(promocode_input, {
2883
- promoCode: promoCode,
2884
- applyPromoCode: applyPromoCode,
2885
- showMultipleTicketTexts: showMultipleTicketTexts,
2886
- removePromoCode: handleRemovePromoCode,
2887
- onPromoCodeChange: handlePromoCodeChange
2888
- }), promoCodeError && Object.values(promoCodeError).map((er, index) => /*#__PURE__*/external_react_default().createElement("div", {
2889
- key: `error-${index}`,
2890
- className: `${ticket_type_index_module.promocodeError} alert alert-danger`
2891
- }, er))), showMultipleTicketTexts && /*#__PURE__*/external_react_default().createElement("a", {
2892
- className: ticket_type_index_module.moreInfo,
2893
- "data-tip": true,
2894
- "data-for": "ticket-quantity-info"
2895
- }, /*#__PURE__*/external_react_default().createElement("i", {
2896
- className: "glyphicon glyphicon-info-sign",
2897
- "aria-hidden": "true"
2898
- }), ` `, "Need multiple ticket types?"), /*#__PURE__*/external_react_default().createElement((external_react_tooltip_default()), {
3297
+ }))))))), !showTicketSelector && /*#__PURE__*/external_react_default().createElement(ticket_notice, {
3298
+ message: noTicketsAvailableMessage || external_i18n_react_default().translate("ticket_type.no_tickets_available"),
3299
+ variant: "info"
3300
+ }), /*#__PURE__*/external_react_default().createElement((external_react_tooltip_default()), {
2899
3301
  id: "ticket-quantity-info",
3302
+ place: "bottom",
2900
3303
  overridePosition: utils/* avoidTooltipOverflow */.kb
2901
3304
  }, /*#__PURE__*/external_react_default().createElement("div", {
2902
3305
  className: ticket_type_index_module.moreInfoTooltip
2903
- }, external_i18n_react_default().translate("ticket_type.ticket_quantity_tooltip"))))), inPersonDisclaimer && ticket && (0,actions/* isInPersonTicketType */.Qc)(ticket) && /*#__PURE__*/external_react_default().createElement("div", {
3306
+ }, external_i18n_react_default().translate("ticket_type.ticket_quantity_tooltip"))), allowPromoCodes && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, /*#__PURE__*/external_react_default().createElement(promocode_input, {
3307
+ promoStatus: promoState.status,
3308
+ promoCode: promoCode,
3309
+ suggestedCode: promoState.suggestedCode,
3310
+ isAutoApplied: promoState.isAutoApplied,
3311
+ onInputChange: promoActions.onInputChange,
3312
+ onApply: handleApplyPromoCode,
3313
+ onRemove: promoActions.onRemove,
3314
+ showMultipleTicketTexts: showMultipleTicketTexts
3315
+ })), /*#__PURE__*/external_react_default().createElement(ticket_notice, {
3316
+ message: validationError,
3317
+ variant: "error"
3318
+ }), /*#__PURE__*/external_react_default().createElement(ticket_notice, {
3319
+ message: infoMessage,
3320
+ variant: "info"
3321
+ }))), inPersonDisclaimer && ticket && (0,actions/* isInPersonTicketType */.Qc)(ticket) && /*#__PURE__*/external_react_default().createElement("div", {
2904
3322
  className: ticket_type_index_module.inPersonDisclaimer
2905
3323
  }, /*#__PURE__*/external_react_default().createElement((raw_html_default()), null, inPersonDisclaimer)))));
2906
3324
  };
@@ -2936,16 +3354,16 @@ function button_bar_defineProperty(obj, key, value) { if (key in obj) { Object.d
2936
3354
  const ButtonBarComponent = ({
2937
3355
  step,
2938
3356
  changeStep,
2939
- validatePromoCode,
2940
- onValidateError,
3357
+ onNextStep,
2941
3358
  formValues,
2942
3359
  removeReservedTicket,
2943
- inPersonDisclaimer
3360
+ inPersonDisclaimer,
3361
+ promoIsReady
2944
3362
  }) => {
2945
3363
  const {
2946
3364
  ticketType,
2947
3365
  ticketQuantity,
2948
- promoCode
3366
+ ticketSelectionValid
2949
3367
  } = formValues || {};
2950
3368
  const nextButtonText = inPersonDisclaimer && ticketType && (0,actions/* isInPersonTicketType */.Qc)(ticketType) ? 'Accept' : 'Next';
2951
3369
  return /*#__PURE__*/external_react_default().createElement("div", {
@@ -2963,12 +3381,11 @@ const ButtonBarComponent = ({
2963
3381
  className: `${button_bar_index_module.button} button`,
2964
3382
  onClick: () => removeReservedTicket()
2965
3383
  }, "< Back"), step === constants.STEP_SELECT_TICKET_TYPE && /*#__PURE__*/external_react_default().createElement("button", {
2966
- disabled: !ticketType,
3384
+ disabled: !ticketSelectionValid || !promoIsReady,
2967
3385
  className: `${button_bar_index_module.button} button`,
2968
- onClick: () => validatePromoCode(button_bar_objectSpread(button_bar_objectSpread({}, ticketType), {}, {
2969
- ticketQuantity,
2970
- promoCode
2971
- }), onValidateError)
3386
+ onClick: () => onNextStep(button_bar_objectSpread(button_bar_objectSpread({}, ticketType), {}, {
3387
+ ticketQuantity
3388
+ }))
2972
3389
  }, nextButtonText), step === constants.STEP_PERSONAL_INFO && ticketType?.cost === 0 && /*#__PURE__*/external_react_default().createElement("button", {
2973
3390
  className: `${button_bar_index_module.button} button`,
2974
3391
  type: "submit",
@@ -3234,10 +3651,10 @@ const TicketOwnedComponent = ({
3234
3651
  const NoAllowedTickets = ({
3235
3652
  noAllowedTicketsMessage
3236
3653
  }) => {
3237
- return /*#__PURE__*/external_react_default().createElement("div", {
3238
- className: no_allowed_tickets_index_module.noAllowedWrapper
3239
- }, /*#__PURE__*/external_react_default().createElement("div", {
3240
- className: `${no_allowed_tickets_index_module.alert} alert alert-warning`,
3654
+ return /*#__PURE__*/React.createElement("div", {
3655
+ className: styles.noAllowedWrapper
3656
+ }, /*#__PURE__*/React.createElement("div", {
3657
+ className: `${styles.alert} alert alert-warning`,
3241
3658
  role: "alert",
3242
3659
  dangerouslySetInnerHTML: {
3243
3660
  __html: noAllowedTicketsMessage
@@ -3245,7 +3662,7 @@ const NoAllowedTickets = ({
3245
3662
  }));
3246
3663
  };
3247
3664
 
3248
- /* harmony default export */ const no_allowed_tickets = (NoAllowedTickets);
3665
+ /* harmony default export */ const no_allowed_tickets = ((/* unused pure expression or super */ null && (NoAllowedTickets)));
3249
3666
  ;// CONCATENATED MODULE: ./src/components/ticket-taxes-error/index.module.scss
3250
3667
  // extracted by mini-css-extract-plugin
3251
3668
  /* harmony default export */ const ticket_taxes_error_index_module = ({"ticketTaxesErrorWrapper":"ticketTaxesErrorWrapper___ldztd","alert":"alert___AM17V"});
@@ -3285,7 +3702,7 @@ const TicketTaxesError = ({
3285
3702
 
3286
3703
  /* harmony default export */ const ticket_taxes_error = (TicketTaxesError);
3287
3704
  ;// CONCATENATED MODULE: ./src/components/registration-form/index.js
3288
- const registration_form_excluded = ["loadSession", "setMarketingSettings", "changeStep", "removeReservedTicket", "reserveTicket", "payTicketWithProvider", "trackEvent", "onPurchaseComplete", "getTicketTypesAndTaxes", "getLoginCode", "passwordlessLogin", "goToLogin", "loginOptions", "allowsNativeAuth", "allowsOtpAuth", "reservation", "checkout", "ticketTypes", "taxTypes", "step", "passwordlessCodeSent", "passwordlessEmail", "passwordlessCode", "passwordlessCodeLifeTime", "getPasswordlessCode", "passwordlessCodeError", "loginWithCode", "goToExtraQuestions", "goToMyOrders", "goToEvent", "profileData", "summitData", "supportEmail", "ticketOwned", "ownedTickets", "widgetLoading", "loading", "inPersonDisclaimer", "userProfile", "handleCompanyError", "providerOptions", "invitation", "loginInitialEmailInputValue", "getMyInvitation", "showMultipleTicketTexts", "noAllowedTicketsMessage", "ticketTaxesErrorMessage", "authErrorCallback", "clearWidgetState", "allowPromoCodes", "showCompanyInput", "companyDDLPlaceholder", "nowUtc", "updateClock", "completedExtraQuestions", "loadProfileData", "closeWidget", "hasVirtualAccessLevel", "hidePostalCode", "onError", "successfulPaymentReturnUrl", "idpLogoLight", "idpLogoDark", "idpLogoAlt", "showCompanyInputDefaultOptions", "companyDDLOptions2Show", "promoCode", "hasDiscount", "getTicketDiscount", "removePromoCode", "applyPromoCode", "validatePromoCode", "closeHandlerRef"];
3705
+ const registration_form_excluded = ["loadSession", "setMarketingSettings", "changeStep", "removeReservedTicket", "reserveTicket", "payTicketWithProvider", "trackEvent", "onPurchaseComplete", "getTicketTypesAndTaxes", "getLoginCode", "passwordlessLogin", "goToLogin", "loginOptions", "allowsNativeAuth", "allowsOtpAuth", "reservation", "checkout", "ticketTypes", "taxTypes", "step", "passwordlessCodeSent", "passwordlessEmail", "passwordlessCode", "passwordlessCodeLifeTime", "getPasswordlessCode", "passwordlessCodeError", "loginWithCode", "goToExtraQuestions", "goToMyOrders", "goToEvent", "profileData", "summitData", "supportEmail", "ticketOwned", "ownedTickets", "widgetLoading", "loading", "inPersonDisclaimer", "userProfile", "handleCompanyError", "providerOptions", "invitation", "loginInitialEmailInputValue", "getMyInvitation", "showMultipleTicketTexts", "noAllowedTicketsMessage", "noTicketsAvailableMessage", "ticketTaxesErrorMessage", "authErrorCallback", "clearWidgetState", "allowPromoCodes", "showCompanyInput", "companyDDLPlaceholder", "nowUtc", "updateClock", "completedExtraQuestions", "loadProfileData", "closeWidget", "hasVirtualAccessLevel", "hidePostalCode", "onError", "successfulPaymentReturnUrl", "idpLogoLight", "idpLogoDark", "idpLogoAlt", "showCompanyInputDefaultOptions", "companyDDLOptions2Show", "promoCode", "promoCodeVerified", "promoCodeValidating", "promoCodeAllowsReassign", "discoveredPromoCodes", "hasDiscount", "getTicketDiscount", "removePromoCode", "applyPromoCode", "validatePromoCode", "discoverPromoCodes", "startWidgetLoading", "stopWidgetLoading", "closeHandlerRef"];
3289
3706
 
3290
3707
  function registration_form_ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
3291
3708
 
@@ -3334,6 +3751,7 @@ function registration_form_objectWithoutPropertiesLoose(source, excluded) { if (
3334
3751
 
3335
3752
 
3336
3753
 
3754
+
3337
3755
 
3338
3756
 
3339
3757
  let language = (0,utils/* getCurrentUserLanguage */.AS)();
@@ -3397,6 +3815,7 @@ const RegistrationFormContent = _ref => {
3397
3815
  getMyInvitation,
3398
3816
  showMultipleTicketTexts,
3399
3817
  noAllowedTicketsMessage,
3818
+ noTicketsAvailableMessage,
3400
3819
  ticketTaxesErrorMessage,
3401
3820
  authErrorCallback,
3402
3821
  clearWidgetState,
@@ -3418,11 +3837,18 @@ const RegistrationFormContent = _ref => {
3418
3837
  showCompanyInputDefaultOptions,
3419
3838
  companyDDLOptions2Show,
3420
3839
  promoCode,
3840
+ promoCodeVerified,
3841
+ promoCodeValidating,
3842
+ promoCodeAllowsReassign,
3843
+ discoveredPromoCodes,
3421
3844
  hasDiscount,
3422
3845
  getTicketDiscount,
3423
3846
  removePromoCode,
3424
3847
  applyPromoCode,
3425
3848
  validatePromoCode,
3849
+ discoverPromoCodes,
3850
+ startWidgetLoading,
3851
+ stopWidgetLoading,
3426
3852
  closeHandlerRef
3427
3853
  } = _ref,
3428
3854
  rest = registration_form_objectWithoutProperties(_ref, registration_form_excluded);
@@ -3439,19 +3865,17 @@ const RegistrationFormContent = _ref => {
3439
3865
  });
3440
3866
  const [ticketDataError, setTicketDataError] = (0,external_react_.useState)(false);
3441
3867
  const [ticketDataLoaded, setTicketDataLoaded] = (0,external_react_.useState)(false);
3868
+ const [unappliedCodeWarning, setUnappliedCodeWarning] = (0,external_react_.useState)(null);
3442
3869
  const {
3443
3870
  values: formValues,
3444
3871
  errors: formErrors
3445
3872
  } = registrationForm;
3446
-
3447
- const setFormValues = values => setRegistrationForm(registration_form_objectSpread(registration_form_objectSpread({}, registrationForm), {}, {
3448
- values
3449
- }));
3450
-
3451
- const setFormErrors = errors => setRegistrationForm(registration_form_objectSpread(registration_form_objectSpread({}, registrationForm), {}, {
3873
+ const mergeFormValues = (0,external_react_.useCallback)(partial => setRegistrationForm(prev => registration_form_objectSpread(registration_form_objectSpread({}, prev), {}, {
3874
+ values: registration_form_objectSpread(registration_form_objectSpread({}, prev.values), partial)
3875
+ })), []);
3876
+ const setFormErrors = (0,external_react_.useCallback)(errors => setRegistrationForm(prev => registration_form_objectSpread(registration_form_objectSpread({}, prev), {}, {
3452
3877
  errors
3453
- }));
3454
-
3878
+ })), []);
3455
3879
  const {
3456
3880
  publicKey,
3457
3881
  provider
@@ -3486,7 +3910,43 @@ const RegistrationFormContent = _ref => {
3486
3910
  }, [registrationForm.values, step]);
3487
3911
  (0,external_react_.useEffect)(() => {
3488
3912
  setFormErrors([]);
3489
- }, [step]);
3913
+ }, [step]); // Discovery: fetch qualifying promo codes after auth
3914
+
3915
+ (0,external_react_.useEffect)(() => {
3916
+ if (profileData && summitData?.id) {
3917
+ discoverPromoCodes(summitData.id);
3918
+ }
3919
+ }, [profileData, summitData?.id]);
3920
+ const handleFormPromoCodeChange = (0,external_react_.useCallback)(code => mergeFormValues({
3921
+ promoCode: code
3922
+ }), [mergeFormValues]);
3923
+ const promo = hooks_usePromoCode({
3924
+ discoveredPromoCodes,
3925
+ promoCode,
3926
+ promoCodeVerified,
3927
+ promoCodeValidating,
3928
+ applyPromoCode,
3929
+ removePromoCode,
3930
+ validatePromoCode,
3931
+ setFormPromoCode: handleFormPromoCodeChange,
3932
+ ticketDataLoaded: ticketDataLoaded && !ticketDataError,
3933
+ hasTickets: allowedTicketTypes.length > 0
3934
+ }); // Local destructure for readability at call sites.
3935
+
3936
+ const {
3937
+ state: promoState,
3938
+ actions: promoActions
3939
+ } = promo; // Error rendered in the promo notice slot — form-level warning layered on top
3940
+ // of the hook's own validation error (API rejection or status-derived).
3941
+
3942
+ const ticketStepError = unappliedCodeWarning ?? promoState.validationError; // Clear the unapplied-code warning once the condition that would have raised it
3943
+ // no longer holds (input cleared, code applied, or a suggestion is showing).
3944
+
3945
+ (0,external_react_.useEffect)(() => {
3946
+ if (!formValues?.promoCode || promoCode || promoState.status === constants.PROMO_STATUS.SUGGESTED) {
3947
+ setUnappliedCodeWarning(null);
3948
+ }
3949
+ }, [formValues?.promoCode, promoCode, promoState.status]);
3490
3950
  const [ref, {
3491
3951
  height
3492
3952
  }] = (0,external_react_use_namespaceObject.useMeasure)();
@@ -3545,12 +4005,28 @@ const RegistrationFormContent = _ref => {
3545
4005
  });
3546
4006
  };
3547
4007
 
3548
- const handleValidatePromocode = (data, onError) => {
3549
- validatePromoCode(data, onError).then(() => {
3550
- trackAddToCart(data);
3551
- }).catch(e => {
3552
- (0,utils/* handleSentryException */.Gj)(e);
3553
- });
4008
+ const handleAdvanceFromTicketStep = async data => {
4009
+ if (formValues?.promoCode && !promoCode && promoState.status !== constants.PROMO_STATUS.SUGGESTED) {
4010
+ setUnappliedCodeWarning(external_i18n_react_default().translate('promo_code.unapplied_code_warning'));
4011
+ return;
4012
+ } // Re-validate manual codes with final quantity before advancing
4013
+
4014
+
4015
+ if (promoCode && !promoState.isDiscoveredCode) {
4016
+ startWidgetLoading();
4017
+ let valid = false;
4018
+
4019
+ try {
4020
+ valid = await promoActions.onRevalidate(formValues.ticketType, data.ticketQuantity);
4021
+ } finally {
4022
+ stopWidgetLoading();
4023
+ }
4024
+
4025
+ if (!valid) return;
4026
+ }
4027
+
4028
+ trackAddToCart(data);
4029
+ changeStep(constants.STEP_PERSONAL_INFO);
3554
4030
  };
3555
4031
 
3556
4032
  const trackViewItem = data => {
@@ -3592,8 +4068,6 @@ const RegistrationFormContent = _ref => {
3592
4068
  }), profileData && ticketDataError && /*#__PURE__*/external_react_default().createElement(ticket_taxes_error, {
3593
4069
  ticketTaxesErrorMessage: ticketTaxesErrorMessage,
3594
4070
  retryTicketTaxes: () => handleGetTicketTypesAndTaxes(summitData?.id)
3595
- }), noAvailableTickets && /*#__PURE__*/external_react_default().createElement(no_allowed_tickets, {
3596
- noAllowedTicketsMessage: noAllowedTicketsMessage
3597
4071
  }), !ticketDataError && /*#__PURE__*/external_react_default().createElement((external_react_default()).Fragment, null, !profileData && !passwordlessCodeSent && /*#__PURE__*/external_react_default().createElement(login["default"], {
3598
4072
  summitData: summitData,
3599
4073
  loginOptions: loginOptions,
@@ -3626,19 +4100,14 @@ const RegistrationFormContent = _ref => {
3626
4100
  reservation: reservation,
3627
4101
  isActive: step === constants.STEP_SELECT_TICKET_TYPE,
3628
4102
  allowPromoCodes: allowPromoCodes,
3629
- applyPromoCode: applyPromoCode,
3630
- removePromoCode: () => {
3631
- setFormErrors({});
3632
- setFormValues(registration_form_objectSpread(registration_form_objectSpread({}, formValues), {}, {
3633
- promoCode: ""
3634
- }));
3635
- removePromoCode();
3636
- },
4103
+ promo: promo,
4104
+ validationError: ticketStepError,
3637
4105
  promoCode: promoCode,
3638
- formErrors: formErrors,
3639
- changeForm: ticketForm => setFormValues(registration_form_objectSpread(registration_form_objectSpread({}, formValues), ticketForm)),
4106
+ promoCodeAllowsReassign: promoCodeAllowsReassign,
4107
+ changeForm: mergeFormValues,
3640
4108
  trackViewItem: trackViewItem,
3641
- showMultipleTicketTexts: showMultipleTicketTexts
4109
+ showMultipleTicketTexts: showMultipleTicketTexts,
4110
+ noTicketsAvailableMessage: noTicketsAvailableMessage
3642
4111
  }), /*#__PURE__*/external_react_default().createElement(personal_information, {
3643
4112
  isActive: step === constants.STEP_PERSONAL_INFO,
3644
4113
  reservation: reservation,
@@ -3646,9 +4115,9 @@ const RegistrationFormContent = _ref => {
3646
4115
  invitation: invitation,
3647
4116
  summitId: summitData.id,
3648
4117
  changeForm: personalInformation => {
3649
- setFormValues(registration_form_objectSpread(registration_form_objectSpread({}, registrationForm.values), {}, {
4118
+ mergeFormValues({
3650
4119
  personalInformation
3651
- }));
4120
+ });
3652
4121
  reserveTicket({
3653
4122
  provider,
3654
4123
  personalInformation: personalInformation,
@@ -3676,7 +4145,8 @@ const RegistrationFormContent = _ref => {
3676
4145
  showCompanyInput: showCompanyInput,
3677
4146
  companyDDLPlaceholder: companyDDLPlaceholder,
3678
4147
  showCompanyInputDefaultOptions: showCompanyInputDefaultOptions,
3679
- companyDDLOptions2Show: companyDDLOptions2Show
4148
+ companyDDLOptions2Show: companyDDLOptions2Show,
4149
+ promoCodeAllowsReassign: promoCodeAllowsReassign
3680
4150
  }), /*#__PURE__*/external_react_default().createElement(external_react_spring_namespaceObject.animated.div, {
3681
4151
  style: registration_form_objectSpread({}, toggleAnimation)
3682
4152
  }, /*#__PURE__*/external_react_default().createElement("div", {
@@ -3697,11 +4167,9 @@ const RegistrationFormContent = _ref => {
3697
4167
  step: step,
3698
4168
  inPersonDisclaimer: inPersonDisclaimer,
3699
4169
  formValues: formValues,
4170
+ promoIsReady: promoState.isReady,
3700
4171
  removeReservedTicket: removeReservedTicket,
3701
- validatePromoCode: handleValidatePromocode,
3702
- onValidateError: {
3703
- onError: (err, res) => setFormErrors(res.body.errors)
3704
- },
4172
+ onNextStep: handleAdvanceFromTicketStep,
3705
4173
  changeStep: changeStep
3706
4174
  })), profileData && step === constants.STEP_COMPLETE && /*#__PURE__*/external_react_default().createElement(purchase_complete, {
3707
4175
  checkout: checkout,
@@ -3744,7 +4212,11 @@ const mapStateToProps = ({
3744
4212
  passwordlessCodeSent: registrationLiteState.passwordless.code_sent,
3745
4213
  passwordlessCodeError: registrationLiteState.passwordless.error,
3746
4214
  nowUtc: registrationLiteState.nowUtc,
3747
- promoCode: registrationLiteState.promoCode
4215
+ promoCode: registrationLiteState.promoCode,
4216
+ promoCodeVerified: registrationLiteState.promoCodeVerified,
4217
+ promoCodeValidating: registrationLiteState.promoCodeValidating,
4218
+ promoCodeAllowsReassign: registrationLiteState.promoCodeAllowsReassign,
4219
+ discoveredPromoCodes: registrationLiteState.discoveredPromoCodes
3748
4220
  });
3749
4221
 
3750
4222
  const RegistrationForm = (0,external_react_redux_.connect)(mapStateToProps, {
@@ -3763,7 +4235,10 @@ const RegistrationForm = (0,external_react_redux_.connect)(mapStateToProps, {
3763
4235
  loadProfileData: actions/* loadProfileData */.Bi,
3764
4236
  applyPromoCode: actions/* applyPromoCode */.gs,
3765
4237
  removePromoCode: actions/* removePromoCode */.$r,
3766
- validatePromoCode: actions/* validatePromoCode */.jn
4238
+ validatePromoCode: actions/* validatePromoCode */.jn,
4239
+ discoverPromoCodes: actions/* discoverPromoCodes */.kM,
4240
+ startWidgetLoading: actions/* startWidgetLoading */.Bq,
4241
+ stopWidgetLoading: actions/* stopWidgetLoading */.Rs
3767
4242
  })(RegistrationFormContent);
3768
4243
  RegistrationForm.defaultProps = {
3769
4244
  loginInitialEmailInputValue: '',
@@ -3836,8 +4311,8 @@ var external_react_default = /*#__PURE__*/__webpack_require__.n(external_react_)
3836
4311
  // EXTERNAL MODULE: external "prop-types"
3837
4312
  var external_prop_types_ = __webpack_require__(580);
3838
4313
  var external_prop_types_default = /*#__PURE__*/__webpack_require__.n(external_prop_types_);
3839
- // EXTERNAL MODULE: ./src/components/registration-form/index.js + 43 modules
3840
- var registration_form = __webpack_require__(558);
4314
+ // EXTERNAL MODULE: ./src/components/registration-form/index.js + 46 modules
4315
+ var registration_form = __webpack_require__(95);
3841
4316
  // EXTERNAL MODULE: ./src/utils/withReduxProvider.js + 9 modules
3842
4317
  var withReduxProvider = __webpack_require__(974);
3843
4318
  ;// CONCATENATED MODULE: ./src/styles/general.module.scss
@@ -3952,6 +4427,7 @@ RegistrationModal.propTypes = {
3952
4427
  initialOrderComplete1stParagraph: (external_prop_types_default()).string,
3953
4428
  initialOrderComplete2ndParagraph: (external_prop_types_default()).string,
3954
4429
  initialOrderCompleteButton: (external_prop_types_default()).string,
4430
+ noTicketsAvailableMessage: (external_prop_types_default()).string,
3955
4431
  orderCompleteTitle: (external_prop_types_default()).string,
3956
4432
  orderComplete1stParagraph: (external_prop_types_default()).string,
3957
4433
  orderComplete2ndParagraph: (external_prop_types_default()).string,
@@ -4044,10 +4520,16 @@ var formatErrorMessage = __webpack_require__(104);
4044
4520
  var utils = __webpack_require__(452);
4045
4521
  ;// CONCATENATED MODULE: ./src/helpers/getTicketMaxQuantity.js
4046
4522
 
4047
- const getTicketMaxQuantity = ticket => {
4523
+ const getTicketMaxQuantity = (ticket, remainingQuantityPerAccount) => {
4048
4524
  if (!ticket) return 0;
4049
4525
  if ((0,utils/* isPrePaidTicketType */.B6)(ticket)) return 1;
4050
- return Math.min((ticket.quantity_2_sell || Number.MAX_SAFE_INTEGER) - ticket.quantity_sold, ticket.max_quantity_per_order || Number.MAX_SAFE_INTEGER);
4526
+ let max = Math.min((ticket.quantity_2_sell ?? Number.MAX_SAFE_INTEGER) - ticket.quantity_sold, ticket.max_quantity_per_order ?? Number.MAX_SAFE_INTEGER);
4527
+
4528
+ if (remainingQuantityPerAccount != null) {
4529
+ max = Math.min(max, remainingQuantityPerAccount);
4530
+ }
4531
+
4532
+ return max;
4051
4533
  };
4052
4534
  ;// CONCATENATED MODULE: ./src/helpers/index.js
4053
4535
 
@@ -4075,6 +4557,7 @@ const getTicketMaxQuantity = ticket => {
4075
4557
  /* harmony export */ "ORDER_STATUS_PAID": () => (/* binding */ ORDER_STATUS_PAID),
4076
4558
  /* harmony export */ "PAYMENT_PROVIDER_LAWPAY": () => (/* binding */ PAYMENT_PROVIDER_LAWPAY),
4077
4559
  /* harmony export */ "PAYMENT_PROVIDER_STRIPE": () => (/* binding */ PAYMENT_PROVIDER_STRIPE),
4560
+ /* harmony export */ "PROMO_STATUS": () => (/* binding */ PROMO_STATUS),
4078
4561
  /* harmony export */ "PURCHASE_COMPLETE": () => (/* binding */ PURCHASE_COMPLETE),
4079
4562
  /* harmony export */ "RESEND_TIME": () => (/* binding */ RESEND_TIME),
4080
4563
  /* harmony export */ "STEP_COMPLETE": () => (/* binding */ STEP_COMPLETE),
@@ -4128,7 +4611,16 @@ const PURCHASE_COMPLETE = 'purchase_complete'; // ERRORS
4128
4611
 
4129
4612
  const ERROR_TYPE_ERROR = 'error_type_error';
4130
4613
  const ERROR_TYPE_VALIDATION = 'error_type_validation';
4131
- const ERROR_TYPE_PAYMENT = 'error_type_payment'; // PROVIDERS
4614
+ const ERROR_TYPE_PAYMENT = 'error_type_payment'; // PROMO CODE STATUS
4615
+
4616
+ const PROMO_STATUS = {
4617
+ IDLE: 'idle',
4618
+ SUGGESTED: 'suggested',
4619
+ APPLYING: 'applying',
4620
+ VALIDATING: 'validating',
4621
+ VALID: 'valid',
4622
+ INVALID: 'invalid'
4623
+ }; // PROVIDERS
4132
4624
 
4133
4625
  const PAYMENT_PROVIDER_STRIPE = 'Stripe';
4134
4626
  const PAYMENT_PROVIDER_LAWPAY = 'LawPay';
@@ -4392,7 +4884,11 @@ const DEFAULT_STATE = {
4392
4884
  userProfile: null
4393
4885
  },
4394
4886
  nowUtc: localNowUtc,
4395
- promoCode: ''
4887
+ promoCode: '',
4888
+ promoCodeVerified: null,
4889
+ promoCodeValidating: false,
4890
+ promoCodeAllowsReassign: true,
4891
+ discoveredPromoCodes: []
4396
4892
  };
4397
4893
 
4398
4894
  const RegistrationLiteReducer = (state = DEFAULT_STATE, action) => {
@@ -4400,7 +4896,6 @@ const RegistrationLiteReducer = (state = DEFAULT_STATE, action) => {
4400
4896
  type,
4401
4897
  payload
4402
4898
  } = action;
4403
- console.log(action);
4404
4899
 
4405
4900
  switch (type) {
4406
4901
  case actions/* CLEAR_WIDGET_STATE */.t$:
@@ -4443,6 +4938,10 @@ const RegistrationLiteReducer = (state = DEFAULT_STATE, action) => {
4443
4938
  requestedTicketTypes: false,
4444
4939
  taxTypes: [],
4445
4940
  invitation: null,
4941
+ promoCode: '',
4942
+ promoCodeVerified: null,
4943
+ promoCodeAllowsReassign: true,
4944
+ discoveredPromoCodes: [],
4446
4945
  passwordless: _objectSpread({}, DEFAULT_STATE.passwordless),
4447
4946
  settings: _objectSpread(_objectSpread({}, DEFAULT_STATE.settings), {}, {
4448
4947
  summitId: summitData.id,
@@ -4547,7 +5046,11 @@ const RegistrationLiteReducer = (state = DEFAULT_STATE, action) => {
4547
5046
  {
4548
5047
  return _objectSpread(_objectSpread({}, state), {}, {
4549
5048
  reservation: null,
4550
- promoCode: ''
5049
+ promoCode: '',
5050
+ promoCodeVerified: null,
5051
+ promoCodeValidating: false,
5052
+ promoCodeAllowsReassign: true,
5053
+ discoveredPromoCodes: []
4551
5054
  });
4552
5055
  }
4553
5056
 
@@ -4558,7 +5061,11 @@ const RegistrationLiteReducer = (state = DEFAULT_STATE, action) => {
4558
5061
  reservation: null,
4559
5062
  userProfile: null,
4560
5063
  invitation: null,
4561
- promoCode: ''
5064
+ promoCode: '',
5065
+ promoCodeVerified: null,
5066
+ promoCodeValidating: false,
5067
+ promoCodeAllowsReassign: true,
5068
+ discoveredPromoCodes: []
4562
5069
  });
4563
5070
  }
4564
5071
 
@@ -4589,7 +5096,10 @@ const RegistrationLiteReducer = (state = DEFAULT_STATE, action) => {
4589
5096
  case actions/* CLEAR_CURRENT_PROMO_CODE */.ZD:
4590
5097
  {
4591
5098
  return _objectSpread(_objectSpread({}, state), {}, {
4592
- promoCode: ''
5099
+ promoCode: '',
5100
+ promoCodeVerified: null,
5101
+ promoCodeValidating: false,
5102
+ promoCodeAllowsReassign: true
4593
5103
  });
4594
5104
  }
4595
5105
 
@@ -4599,7 +5109,52 @@ const RegistrationLiteReducer = (state = DEFAULT_STATE, action) => {
4599
5109
  currentPromoCode
4600
5110
  } = payload;
4601
5111
  return _objectSpread(_objectSpread({}, state), {}, {
4602
- promoCode: currentPromoCode
5112
+ promoCode: currentPromoCode,
5113
+ promoCodeVerified: null,
5114
+ promoCodeValidating: false,
5115
+ promoCodeAllowsReassign: true
5116
+ });
5117
+ }
5118
+
5119
+ case actions/* VALIDATE_PROMO_CODE */.j6:
5120
+ {
5121
+ return _objectSpread(_objectSpread({}, state), {}, {
5122
+ promoCodeValidating: true
5123
+ });
5124
+ }
5125
+
5126
+ case actions/* VALIDATE_PROMO_CODE_SUCCESS */.w6:
5127
+ {
5128
+ const {
5129
+ allows_to_reassign
5130
+ } = payload.response;
5131
+ return _objectSpread(_objectSpread({}, state), {}, {
5132
+ promoCodeVerified: true,
5133
+ promoCodeValidating: false,
5134
+ promoCodeAllowsReassign: allows_to_reassign ?? true
5135
+ });
5136
+ }
5137
+
5138
+ case actions/* VALIDATE_PROMO_CODE_ERROR */.o5:
5139
+ {
5140
+ return _objectSpread(_objectSpread({}, state), {}, {
5141
+ promoCodeVerified: false,
5142
+ promoCodeValidating: false,
5143
+ promoCodeAllowsReassign: true
5144
+ });
5145
+ }
5146
+
5147
+ case actions/* VALIDATE_PROMO_CODE_RATE_LIMITED */.Tk:
5148
+ {
5149
+ return _objectSpread(_objectSpread({}, state), {}, {
5150
+ promoCodeValidating: false
5151
+ });
5152
+ }
5153
+
5154
+ case actions/* DISCOVER_PROMO_CODES_SUCCESS */.dQ:
5155
+ {
5156
+ return _objectSpread(_objectSpread({}, state), {}, {
5157
+ discoveredPromoCodes: payload.response?.data || []
4603
5158
  });
4604
5159
  }
4605
5160
 
@@ -4717,13 +5272,6 @@ module.exports = "data:image/svg+xml,%3c!-- Generator: Adobe Illustrator 25.4.1,
4717
5272
 
4718
5273
  /***/ }),
4719
5274
 
4720
- /***/ 60:
4721
- /***/ ((module) => {
4722
-
4723
- module.exports = "data:image/svg+xml,%3csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e %3cpath d='M8.11475 2.60519C9.64837 2.25868 11.2529 2.41721 12.6891 3.05713C13.0978 3.23923 13.5767 3.05555 13.7588 2.64686C13.9409 2.23817 13.7572 1.75925 13.3485 1.57714C11.5932 0.795024 9.6321 0.601267 7.75767 1.02477C5.88325 1.44827 4.19593 2.46634 2.94738 3.92714C1.69883 5.38795 0.955938 7.21321 0.829498 9.13072C0.703058 11.0482 1.19985 12.9552 2.24577 14.5673C3.2917 16.1794 4.83072 17.4103 6.6333 18.0762C8.43589 18.7422 10.4055 18.8076 12.2483 18.2627C14.0911 17.7179 15.7084 16.5919 16.859 15.0528C18.0096 13.5137 18.6319 11.6438 18.633 9.72216V8.97638C18.633 8.52896 18.2703 8.16625 17.8228 8.16625C17.3754 8.16625 17.0127 8.52896 17.0127 8.97638V9.72123C17.0118 11.2935 16.5027 12.8234 15.5613 14.0827C14.6199 15.342 13.2966 16.2632 11.7889 16.709C10.2811 17.1548 8.66965 17.1012 7.19481 16.5564C5.71996 16.0115 4.46077 15.0045 3.60501 13.6855C2.74925 12.3665 2.34279 10.8062 2.44624 9.23733C2.54969 7.66846 3.15751 6.17506 4.17905 4.97986C5.2006 3.78465 6.58112 2.95169 8.11475 2.60519Z' fill='%2392CD76'/%3e %3cpath d='M18.396 3.81325C18.7122 3.49672 18.7119 2.98378 18.3954 2.66756C18.0789 2.35134 17.5659 2.3516 17.2497 2.66813L9.72129 10.2041L7.86404 8.34683C7.54767 8.03046 7.03472 8.03046 6.71835 8.34683C6.40197 8.66321 6.40197 9.17615 6.71835 9.49253L9.14873 11.9229C9.30071 12.0749 9.50685 12.1602 9.72178 12.1602C9.93671 12.1601 10.1428 12.0747 10.2947 11.9226L18.396 3.81325Z' fill='%2392CD76'/%3e %3c/svg%3e"
4724
-
4725
- /***/ }),
4726
-
4727
5275
  /***/ 267:
4728
5276
  /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
4729
5277
 
@@ -4789,7 +5337,7 @@ module.exports = require("sweetalert2");
4789
5337
  /***/ ((module) => {
4790
5338
 
4791
5339
  "use strict";
4792
- module.exports = JSON.parse('{"purchase_complete_step":{"title":" Your order is complete","initial_order_complete_1st_paragraph_label":"A ticket has been assigned to {attendee}. To complete {adv} additional ticket details, please click the \\"{button}\\" button.","initial_order_complete_button":"Finish Now","order_complete_1st_paragraph_label":"You may visit the \\"My Orders/Tickets\\" tab in the top right-hand corner of the navigation bar to\\n assign/reassign tickets or to complete any required ticket details.","order_complete_button":"View My Orders/Tickets","access_event_button":"Access Event Now","initial_order_footer_label":"If you wish to transfer your assigned ticket, close this window and visit the \\"My Orders/Tickets\\" tab in the top navigation bar. ","footer_assistance_text":"For further assistance, please email <a href=\\"mailto:{supportEmail}\\">{supportEmail}</a>","event_will_start_text":"The event will start on {date} at {time} {time_zone_label}"},"ticket_type":{"ticket_quantity_tooltip":"Only one ticket type can be selected per order. To purchase multiple ticket types, please place a separate registration order for each ticket type."},"promo_code":{"promo_code_tooltip":"Only one promo code can be used per order; the code will be applied to all tickets in this order. If you\'d like to use multiple promo codes, please place a separate registration order for each promo code."}}');
5340
+ module.exports = JSON.parse('{"purchase_complete_step":{"title":" Your order is complete","initial_order_complete_1st_paragraph_label":"A ticket has been assigned to {attendee}. To complete {adv} additional ticket details, please click the \\"{button}\\" button.","initial_order_complete_button":"Finish Now","order_complete_1st_paragraph_label":"You may visit the \\"My Orders/Tickets\\" tab in the top right-hand corner of the navigation bar to\\n assign/reassign tickets or to complete any required ticket details.","order_complete_button":"View My Orders/Tickets","access_event_button":"Access Event Now","initial_order_footer_label":"If you wish to transfer your assigned ticket, close this window and visit the \\"My Orders/Tickets\\" tab in the top navigation bar. ","footer_assistance_text":"For further assistance, please email <a href=\\"mailto:{supportEmail}\\">{supportEmail}</a>","event_will_start_text":"The event will start on {date} at {time} {time_zone_label}"},"ticket_type":{"ticket_quantity_tooltip":"Only one ticket type can be selected per order. To purchase multiple ticket types, please place a separate registration order for each ticket type.","no_tickets_available":"There are no tickets currently available for purchase. If you have a promo code, enter it below to check for eligible tickets.","max_per_order_one":"This ticket type is limited to 1 per order.","max_per_order_other":"This ticket type is limited to {limit} per order."},"promo_code":{"promo_code_tooltip":"Only one promo code can be used per order; the code will be applied to all tickets in this order. If you\'d like to use multiple promo codes, please place a separate registration order for each promo code.","default_label":"Do you have a promo code?","auto_applied_label":"Following promo code was automatically applied:","applied_label":"Applied promo code:","applying_label":"Applying promo code...","suggestion_label":"You qualify for the following promo code:","per_account_limit_one":"Promo code limits {limit} ticket per account.","per_account_limit_other":"Promo code limits {limit} tickets per account.","non_transferable":"This ticket will be automatically assigned to you and cannot be reassigned.","unapplied_code_warning":"You entered a promo code but it hasn\'t been applied. Make sure to click the \'Apply\' button or remove it before continuing.","invalid_code":"Promo code entered is not valid.","validation_error":"An error occurred while validating the promo code."}}');
4793
5341
 
4794
5342
  /***/ })
4795
5343
 
@@ -4876,7 +5424,7 @@ __webpack_require__.r(__webpack_exports__);
4876
5424
  /* harmony import */ var _login__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(462);
4877
5425
  /* harmony import */ var _login_passwordless__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(590);
4878
5426
  /* harmony import */ var _registration_modal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(889);
4879
- /* harmony import */ var _registration_form__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(558);
5427
+ /* harmony import */ var _registration_form__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(95);
4880
5428
  /**
4881
5429
  * Copyright 2026 OpenStack Foundation
4882
5430
  * Licensed under the Apache License, Version 2.0 (the "License");