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