summit-registration-lite 7.0.6 → 7.0.7

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.
@@ -1486,10 +1486,12 @@ const usePromoCode = ({
1486
1486
  //
1487
1487
  // Note on concurrency: the two call sites (early-auto-apply effect and
1488
1488
  // onTicketSelected's auto-apply branch) operate on disjoint states by design
1489
- // (the effect requires `!hasTickets`, the branch requires a selected ticket),
1490
- // so a true concurrent invocation is unreachable in practice. If a future
1491
- // change makes that overlap possible, gate this body with a ref-tracked
1492
- // in-flight flag rather than `applyingCode` (which is captured stale here).
1489
+ // (the effect requires `!isApplied`, the branch fires only on a user-driven
1490
+ // ticket selection by which point isApplied is already true if early
1491
+ // auto-apply ran), so a true concurrent invocation is unreachable in
1492
+ // practice. If a future change makes that overlap possible, gate this body
1493
+ // with a ref-tracked in-flight flag rather than `applyingCode` (which is
1494
+ // captured stale here).
1493
1495
 
1494
1496
  const tryAutoApply = (0,external_react_.useCallback)(async ticket => {
1495
1497
  setIsAutoApplied(true);
@@ -1517,8 +1519,14 @@ const usePromoCode = ({
1517
1519
  }
1518
1520
  }, [discoveredPromoCode, applyPromoCode, onRevalidate, handleValidationError]);
1519
1521
  const onTicketSelected = (0,external_react_.useCallback)(async ticket => {
1520
- const qualifies = discoveredPromoCode && isCodeValidForTicket(ticket);
1521
- setSuggestionActive(qualifies);
1522
+ const qualifies = discoveredPromoCode && isCodeValidForTicket(ticket); // Only turn the suggestion on when the current ticket qualifies; don't
1523
+ // turn it off otherwise. With auto-switch-on-Apply, the suggestion is
1524
+ // still useful when the user has a non-qualifying ticket selected
1525
+ // (Apply will switch them to the qualifying one). The status logic
1526
+ // hides the banner whenever a code is applied, so leaving this true
1527
+ // doesn't surface a stale suggestion.
1528
+
1529
+ if (qualifies) setSuggestionActive(true);
1522
1530
  setSuggestionDismissed(false);
1523
1531
  setManualError(null); // Manual (non-discovered) code is applied: re-validate for new ticket
1524
1532
 
@@ -1527,17 +1535,15 @@ const usePromoCode = ({
1527
1535
  return;
1528
1536
  }
1529
1537
 
1530
- if (!discoveredPromoCode) return; // Discovered code is currently applied
1538
+ if (!discoveredPromoCode) return; // Discovered code is currently applied — re-validate against the new
1539
+ // ticket. The backend rejects if the code doesn't apply, surfacing
1540
+ // INVALID to the user (they can then choose to Remove or pick another
1541
+ // ticket). Previously we silently removed the code on a non-qualifying
1542
+ // pick, which hid the rejection.
1531
1543
 
1532
1544
  if (isDiscoveredCode) {
1533
- if (!qualifies) {
1534
- setIsAutoApplied(false);
1535
- removePromoCode();
1536
- } else {
1537
- const valid = await onRevalidate(ticket, 1);
1538
- if (!valid) setIsAutoApplied(false);
1539
- }
1540
-
1545
+ const valid = await onRevalidate(ticket, 1);
1546
+ if (!valid) setIsAutoApplied(false);
1541
1547
  return;
1542
1548
  } // No code applied, ticket qualifies, auto-apply configured, single code only
1543
1549
 
@@ -1545,19 +1551,20 @@ const usePromoCode = ({
1545
1551
  if (!isApplied && qualifies && discoveredPromoCode.auto_apply && !userRemovedAutoApply && discoveredPromoCodes.length === 1) {
1546
1552
  await tryAutoApply(ticket);
1547
1553
  }
1548
- }, [discoveredPromoCode, isApplied, isDiscoveredCode, userRemovedAutoApply, discoveredPromoCodes, isCodeValidForTicket, removePromoCode, onRevalidate, tryAutoApply]); // Early auto-apply: when no tickets are available and a single auto_apply code
1549
- // was discovered, apply it so the API returns WithPromoCode ticket types.
1550
- // On failure, mark as removed to prevent re-fire loops.
1554
+ }, [discoveredPromoCode, isApplied, isDiscoveredCode, userRemovedAutoApply, discoveredPromoCodes, isCodeValidForTicket, onRevalidate, tryAutoApply]); // Early auto-apply on load (per SDS: silently apply a discovered single
1555
+ // auto_apply code when no code is currently applied). Wait on
1556
+ // ticketDataLoaded only to avoid racing the initial fetch. On failure,
1557
+ // mark as removed to prevent re-fire loops.
1551
1558
 
1552
1559
  (0,external_react_.useEffect)(() => {
1553
1560
  if (userRemovedAutoApply || isApplied) return;
1554
- if (!ticketDataLoaded || hasTickets) return;
1561
+ if (!ticketDataLoaded) return;
1555
1562
  if (!discoveredPromoCode?.auto_apply) return;
1556
1563
  if (discoveredPromoCodes.length !== 1) return;
1557
1564
  tryAutoApply(null).then(success => {
1558
1565
  if (!success) setUserRemovedAutoApply(true);
1559
1566
  });
1560
- }, [userRemovedAutoApply, ticketDataLoaded, hasTickets, discoveredPromoCode, discoveredPromoCodes, isApplied, tryAutoApply]);
1567
+ }, [userRemovedAutoApply, ticketDataLoaded, discoveredPromoCode, discoveredPromoCodes, isApplied, tryAutoApply]);
1561
1568
  const onApply = (0,external_react_.useCallback)(async (code, ticket, quantity) => {
1562
1569
  setManualError(null);
1563
1570
  setApplyingCode(true);
@@ -1596,11 +1603,20 @@ const usePromoCode = ({
1596
1603
  status,
1597
1604
  isReady,
1598
1605
  validationError,
1606
+ // True while applyPromoCode is in flight (covers the window where
1607
+ // promoCode is set but the refreshed ticketTypes haven't landed
1608
+ // yet). Callers should defer ticket-list-driven side effects
1609
+ // until this clears to avoid acting on a stale list.
1610
+ applyingCode,
1599
1611
  // Applied code origin
1600
1612
  isDiscoveredCode,
1601
1613
  isAutoApplied,
1602
1614
  // Discovery / suggestion
1603
1615
  suggestedCode,
1616
+ // Predicate the caller can use to find a ticket the discovered
1617
+ // promo applies to (auto-selection after auto-apply, ticket
1618
+ // switching on Apply).
1619
+ isCodeValidForTicket,
1604
1620
  // Quantity caps from the active discovered code
1605
1621
  maxQuantityFromPromo,
1606
1622
  perAccountLimit
@@ -2891,6 +2907,7 @@ const PromoCodeInput = ({
2891
2907
  promoCode,
2892
2908
  suggestedCode,
2893
2909
  isAutoApplied,
2910
+ isCurrentTicketQualifying,
2894
2911
  onApply,
2895
2912
  onRemove,
2896
2913
  onInputChange,
@@ -2923,12 +2940,16 @@ const PromoCodeInput = ({
2923
2940
  return undefined;
2924
2941
 
2925
2942
  case constants.PROMO_STATUS.SUGGESTED:
2926
- return external_i18n_react_default().translate('promo_code.suggestion_label');
2943
+ // The suggestion banner stays visible across non-qualifying ticket
2944
+ // picks (so the "Apply will switch you" affordance persists). Swap
2945
+ // the copy so it stays accurate: tell the user the code applies to
2946
+ // a different ticket and that Apply will switch them.
2947
+ return isCurrentTicketQualifying ? external_i18n_react_default().translate('promo_code.suggestion_label') : external_i18n_react_default().translate('promo_code.suggestion_label_switch');
2927
2948
 
2928
2949
  default:
2929
2950
  return undefined;
2930
2951
  }
2931
- }, [promoStatus, isAutoApplied]);
2952
+ }, [promoStatus, isAutoApplied, isCurrentTicketQualifying]);
2932
2953
  const canApply = !isLocked && !!inputValue;
2933
2954
 
2934
2955
  const handleInputChange = value => {
@@ -3129,9 +3150,19 @@ const TicketTypeComponent = ({
3129
3150
  // When promo code changes, the API returns updated ticket types with/without discount.
3130
3151
  // Sync the selected ticket with the refreshed data.
3131
3152
  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]);
3153
+ // Auto-select after a promo code is applied. Defer while applyingCode is
3154
+ // true: the reducer sets promoCode synchronously before the refreshed
3155
+ // ticketTypes land, so acting now would auto-select from a stale list.
3156
+ // Once apply settles, prefer the first ticket the discovered code applies
3157
+ // to (so per-ticket validation succeeds); fall back to the only ticket
3158
+ // when there's a single option.
3159
+ // Scan allowedTicketTypes (date-filtered) so we never auto-select
3160
+ // a ticket the user couldn't have picked themselves from the dropdown.
3161
+ if (promoCode && !promoState.applyingCode && allowedTicketTypes.length > 0) {
3162
+ const isValid = promoState.isCodeValidForTicket;
3163
+ const qualifying = isValid && allowedTicketTypes.find(isValid);
3164
+ const toSelect = qualifying || (allowedTicketTypes.length === 1 ? allowedTicketTypes[0] : null);
3165
+ if (toSelect) handleTicketChange(toSelect);
3135
3166
  }
3136
3167
 
3137
3168
  return;
@@ -3148,7 +3179,7 @@ const TicketTypeComponent = ({
3148
3179
  setTicket(null);
3149
3180
  setQuantity(minQuantity);
3150
3181
  }
3151
- }, [promoCode, originalTicketTypes]);
3182
+ }, [promoCode, promoState.applyingCode, allowedTicketTypes, originalTicketTypes]);
3152
3183
  const showTicketSelector = allowedTicketTypes.length > 0;
3153
3184
  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
3185
 
@@ -3199,7 +3230,24 @@ const TicketTypeComponent = ({
3199
3230
  const decrementQuantity = () => setQuantity(quantity - 1);
3200
3231
 
3201
3232
  const handleApplyPromoCode = async code => {
3202
- await promoActions.onApply(code, ticket, quantity);
3233
+ // If the user applies the suggested/discovered code while a
3234
+ // non-qualifying ticket is selected, deselect first. The qualifying
3235
+ // ticket may not yet be in originalTicketTypes (it's revealed by the
3236
+ // code-filtered refetch inside applyPromoCode). After apply resolves,
3237
+ // the post-apply effect picks the qualifying ticket from the
3238
+ // refreshed list. Manual non-discovered codes still validate against
3239
+ // the current ticket — backend decides.
3240
+ const isDiscovered = code === promoState.suggestedCode;
3241
+ const isValid = promoState.isCodeValidForTicket;
3242
+ let targetTicket = ticket;
3243
+
3244
+ if (isDiscovered && isValid && ticket && !isValid(ticket)) {
3245
+ setTicket(null);
3246
+ setQuantity(minQuantity);
3247
+ targetTicket = null;
3248
+ }
3249
+
3250
+ await promoActions.onApply(code, targetTicket, quantity);
3203
3251
  };
3204
3252
 
3205
3253
  return /*#__PURE__*/external_react_default().createElement("div", {
@@ -3309,6 +3357,7 @@ const TicketTypeComponent = ({
3309
3357
  promoCode: promoCode,
3310
3358
  suggestedCode: promoState.suggestedCode,
3311
3359
  isAutoApplied: promoState.isAutoApplied,
3360
+ isCurrentTicketQualifying: promoState.isCodeValidForTicket(ticket),
3312
3361
  onInputChange: promoActions.onInputChange,
3313
3362
  onApply: handleApplyPromoCode,
3314
3363
  onRemove: promoActions.onRemove,
@@ -3914,14 +3963,18 @@ const RegistrationFormContent = _ref => {
3914
3963
  } = promo; // Error rendered in the promo notice slot — form-level warning layered on top
3915
3964
  // of the hook's own validation error (API rejection or status-derived).
3916
3965
 
3917
- const ticketStepError = unappliedCodeWarning ?? promoState.validationError; // Clear the unapplied-code warning once the condition that would have raised it
3918
- // no longer holds (input cleared, code applied, or a suggestion is showing).
3966
+ const ticketStepError = unappliedCodeWarning ?? promoState.validationError; // Clear the unapplied-code warning once it's no longer the right message to show.
3967
+ // The hook's own validationError takes precedence: if the user actually clicked
3968
+ // Apply and the backend rejected, surfacing "you didn't apply" would mask the
3969
+ // specific rejection reason. Depending on validationError (rather than on
3970
+ // applyPromoCode's brief Redux promoCode=truthy window) keeps the cleanup
3971
+ // robust against refactors of the apply action's sequencing.
3919
3972
 
3920
3973
  (0,external_react_.useEffect)(() => {
3921
- if (!formValues?.promoCode || promoCode || promoState.status === constants.PROMO_STATUS.SUGGESTED) {
3974
+ if (!formValues?.promoCode || promoCode || promoState.status === constants.PROMO_STATUS.SUGGESTED || promoState.validationError) {
3922
3975
  setUnappliedCodeWarning(null);
3923
3976
  }
3924
- }, [formValues?.promoCode, promoCode, promoState.status]);
3977
+ }, [formValues?.promoCode, promoCode, promoState.status, promoState.validationError]);
3925
3978
  const [ref, {
3926
3979
  height
3927
3980
  }] = (0,external_react_use_namespaceObject.useMeasure)();
@@ -5303,7 +5356,7 @@ module.exports = require("sweetalert2");
5303
5356
  /***/ ((module) => {
5304
5357
 
5305
5358
  "use strict";
5306
- 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."}}');
5359
+ 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:","suggestion_label_switch":"Following promo code applies to a different ticket. Apply to switch.","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."}}');
5307
5360
 
5308
5361
  /***/ })
5309
5362