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.
@@ -2234,7 +2234,7 @@ module.exports = require("sweetalert2");
2234
2234
  /***/ ((module) => {
2235
2235
 
2236
2236
  "use strict";
2237
- 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."}}');
2237
+ 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."}}');
2238
2238
 
2239
2239
  /***/ })
2240
2240
 
@@ -2458,10 +2458,12 @@ const usePromoCode = ({
2458
2458
  //
2459
2459
  // Note on concurrency: the two call sites (early-auto-apply effect and
2460
2460
  // onTicketSelected's auto-apply branch) operate on disjoint states by design
2461
- // (the effect requires `!hasTickets`, the branch requires a selected ticket),
2462
- // so a true concurrent invocation is unreachable in practice. If a future
2463
- // change makes that overlap possible, gate this body with a ref-tracked
2464
- // in-flight flag rather than `applyingCode` (which is captured stale here).
2461
+ // (the effect requires `!isApplied`, the branch fires only on a user-driven
2462
+ // ticket selection by which point isApplied is already true if early
2463
+ // auto-apply ran), so a true concurrent invocation is unreachable in
2464
+ // practice. If a future change makes that overlap possible, gate this body
2465
+ // with a ref-tracked in-flight flag rather than `applyingCode` (which is
2466
+ // captured stale here).
2465
2467
 
2466
2468
  const tryAutoApply = (0,external_react_.useCallback)(async ticket => {
2467
2469
  setIsAutoApplied(true);
@@ -2489,8 +2491,14 @@ const usePromoCode = ({
2489
2491
  }
2490
2492
  }, [discoveredPromoCode, applyPromoCode, onRevalidate, handleValidationError]);
2491
2493
  const onTicketSelected = (0,external_react_.useCallback)(async ticket => {
2492
- const qualifies = discoveredPromoCode && isCodeValidForTicket(ticket);
2493
- setSuggestionActive(qualifies);
2494
+ const qualifies = discoveredPromoCode && isCodeValidForTicket(ticket); // Only turn the suggestion on when the current ticket qualifies; don't
2495
+ // turn it off otherwise. With auto-switch-on-Apply, the suggestion is
2496
+ // still useful when the user has a non-qualifying ticket selected
2497
+ // (Apply will switch them to the qualifying one). The status logic
2498
+ // hides the banner whenever a code is applied, so leaving this true
2499
+ // doesn't surface a stale suggestion.
2500
+
2501
+ if (qualifies) setSuggestionActive(true);
2494
2502
  setSuggestionDismissed(false);
2495
2503
  setManualError(null); // Manual (non-discovered) code is applied: re-validate for new ticket
2496
2504
 
@@ -2499,17 +2507,15 @@ const usePromoCode = ({
2499
2507
  return;
2500
2508
  }
2501
2509
 
2502
- if (!discoveredPromoCode) return; // Discovered code is currently applied
2510
+ if (!discoveredPromoCode) return; // Discovered code is currently applied — re-validate against the new
2511
+ // ticket. The backend rejects if the code doesn't apply, surfacing
2512
+ // INVALID to the user (they can then choose to Remove or pick another
2513
+ // ticket). Previously we silently removed the code on a non-qualifying
2514
+ // pick, which hid the rejection.
2503
2515
 
2504
2516
  if (isDiscoveredCode) {
2505
- if (!qualifies) {
2506
- setIsAutoApplied(false);
2507
- removePromoCode();
2508
- } else {
2509
- const valid = await onRevalidate(ticket, 1);
2510
- if (!valid) setIsAutoApplied(false);
2511
- }
2512
-
2517
+ const valid = await onRevalidate(ticket, 1);
2518
+ if (!valid) setIsAutoApplied(false);
2513
2519
  return;
2514
2520
  } // No code applied, ticket qualifies, auto-apply configured, single code only
2515
2521
 
@@ -2517,19 +2523,20 @@ const usePromoCode = ({
2517
2523
  if (!isApplied && qualifies && discoveredPromoCode.auto_apply && !userRemovedAutoApply && discoveredPromoCodes.length === 1) {
2518
2524
  await tryAutoApply(ticket);
2519
2525
  }
2520
- }, [discoveredPromoCode, isApplied, isDiscoveredCode, userRemovedAutoApply, discoveredPromoCodes, isCodeValidForTicket, removePromoCode, onRevalidate, tryAutoApply]); // Early auto-apply: when no tickets are available and a single auto_apply code
2521
- // was discovered, apply it so the API returns WithPromoCode ticket types.
2522
- // On failure, mark as removed to prevent re-fire loops.
2526
+ }, [discoveredPromoCode, isApplied, isDiscoveredCode, userRemovedAutoApply, discoveredPromoCodes, isCodeValidForTicket, onRevalidate, tryAutoApply]); // Early auto-apply on load (per SDS: silently apply a discovered single
2527
+ // auto_apply code when no code is currently applied). Wait on
2528
+ // ticketDataLoaded only to avoid racing the initial fetch. On failure,
2529
+ // mark as removed to prevent re-fire loops.
2523
2530
 
2524
2531
  (0,external_react_.useEffect)(() => {
2525
2532
  if (userRemovedAutoApply || isApplied) return;
2526
- if (!ticketDataLoaded || hasTickets) return;
2533
+ if (!ticketDataLoaded) return;
2527
2534
  if (!discoveredPromoCode?.auto_apply) return;
2528
2535
  if (discoveredPromoCodes.length !== 1) return;
2529
2536
  tryAutoApply(null).then(success => {
2530
2537
  if (!success) setUserRemovedAutoApply(true);
2531
2538
  });
2532
- }, [userRemovedAutoApply, ticketDataLoaded, hasTickets, discoveredPromoCode, discoveredPromoCodes, isApplied, tryAutoApply]);
2539
+ }, [userRemovedAutoApply, ticketDataLoaded, discoveredPromoCode, discoveredPromoCodes, isApplied, tryAutoApply]);
2533
2540
  const onApply = (0,external_react_.useCallback)(async (code, ticket, quantity) => {
2534
2541
  setManualError(null);
2535
2542
  setApplyingCode(true);
@@ -2568,11 +2575,20 @@ const usePromoCode = ({
2568
2575
  status,
2569
2576
  isReady,
2570
2577
  validationError,
2578
+ // True while applyPromoCode is in flight (covers the window where
2579
+ // promoCode is set but the refreshed ticketTypes haven't landed
2580
+ // yet). Callers should defer ticket-list-driven side effects
2581
+ // until this clears to avoid acting on a stale list.
2582
+ applyingCode,
2571
2583
  // Applied code origin
2572
2584
  isDiscoveredCode,
2573
2585
  isAutoApplied,
2574
2586
  // Discovery / suggestion
2575
2587
  suggestedCode,
2588
+ // Predicate the caller can use to find a ticket the discovered
2589
+ // promo applies to (auto-selection after auto-apply, ticket
2590
+ // switching on Apply).
2591
+ isCodeValidForTicket,
2576
2592
  // Quantity caps from the active discovered code
2577
2593
  maxQuantityFromPromo,
2578
2594
  perAccountLimit
@@ -3863,6 +3879,7 @@ const PromoCodeInput = ({
3863
3879
  promoCode,
3864
3880
  suggestedCode,
3865
3881
  isAutoApplied,
3882
+ isCurrentTicketQualifying,
3866
3883
  onApply,
3867
3884
  onRemove,
3868
3885
  onInputChange,
@@ -3895,12 +3912,16 @@ const PromoCodeInput = ({
3895
3912
  return undefined;
3896
3913
 
3897
3914
  case constants.PROMO_STATUS.SUGGESTED:
3898
- return external_i18n_react_default().translate('promo_code.suggestion_label');
3915
+ // The suggestion banner stays visible across non-qualifying ticket
3916
+ // picks (so the "Apply will switch you" affordance persists). Swap
3917
+ // the copy so it stays accurate: tell the user the code applies to
3918
+ // a different ticket and that Apply will switch them.
3919
+ return isCurrentTicketQualifying ? external_i18n_react_default().translate('promo_code.suggestion_label') : external_i18n_react_default().translate('promo_code.suggestion_label_switch');
3899
3920
 
3900
3921
  default:
3901
3922
  return undefined;
3902
3923
  }
3903
- }, [promoStatus, isAutoApplied]);
3924
+ }, [promoStatus, isAutoApplied, isCurrentTicketQualifying]);
3904
3925
  const canApply = !isLocked && !!inputValue;
3905
3926
 
3906
3927
  const handleInputChange = value => {
@@ -4101,9 +4122,19 @@ const TicketTypeComponent = ({
4101
4122
  // When promo code changes, the API returns updated ticket types with/without discount.
4102
4123
  // Sync the selected ticket with the refreshed data.
4103
4124
  if (!ticket) {
4104
- // Auto-select if only one ticket type available after promo code applied
4105
- if (promoCode && originalTicketTypes.length === 1) {
4106
- handleTicketChange(originalTicketTypes[0]);
4125
+ // Auto-select after a promo code is applied. Defer while applyingCode is
4126
+ // true: the reducer sets promoCode synchronously before the refreshed
4127
+ // ticketTypes land, so acting now would auto-select from a stale list.
4128
+ // Once apply settles, prefer the first ticket the discovered code applies
4129
+ // to (so per-ticket validation succeeds); fall back to the only ticket
4130
+ // when there's a single option.
4131
+ // Scan allowedTicketTypes (date-filtered) so we never auto-select
4132
+ // a ticket the user couldn't have picked themselves from the dropdown.
4133
+ if (promoCode && !promoState.applyingCode && allowedTicketTypes.length > 0) {
4134
+ const isValid = promoState.isCodeValidForTicket;
4135
+ const qualifying = isValid && allowedTicketTypes.find(isValid);
4136
+ const toSelect = qualifying || (allowedTicketTypes.length === 1 ? allowedTicketTypes[0] : null);
4137
+ if (toSelect) handleTicketChange(toSelect);
4107
4138
  }
4108
4139
 
4109
4140
  return;
@@ -4120,7 +4151,7 @@ const TicketTypeComponent = ({
4120
4151
  setTicket(null);
4121
4152
  setQuantity(minQuantity);
4122
4153
  }
4123
- }, [promoCode, originalTicketTypes]);
4154
+ }, [promoCode, promoState.applyingCode, allowedTicketTypes, originalTicketTypes]);
4124
4155
  const showTicketSelector = allowedTicketTypes.length > 0;
4125
4156
  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
4126
4157
 
@@ -4171,7 +4202,24 @@ const TicketTypeComponent = ({
4171
4202
  const decrementQuantity = () => setQuantity(quantity - 1);
4172
4203
 
4173
4204
  const handleApplyPromoCode = async code => {
4174
- await promoActions.onApply(code, ticket, quantity);
4205
+ // If the user applies the suggested/discovered code while a
4206
+ // non-qualifying ticket is selected, deselect first. The qualifying
4207
+ // ticket may not yet be in originalTicketTypes (it's revealed by the
4208
+ // code-filtered refetch inside applyPromoCode). After apply resolves,
4209
+ // the post-apply effect picks the qualifying ticket from the
4210
+ // refreshed list. Manual non-discovered codes still validate against
4211
+ // the current ticket — backend decides.
4212
+ const isDiscovered = code === promoState.suggestedCode;
4213
+ const isValid = promoState.isCodeValidForTicket;
4214
+ let targetTicket = ticket;
4215
+
4216
+ if (isDiscovered && isValid && ticket && !isValid(ticket)) {
4217
+ setTicket(null);
4218
+ setQuantity(minQuantity);
4219
+ targetTicket = null;
4220
+ }
4221
+
4222
+ await promoActions.onApply(code, targetTicket, quantity);
4175
4223
  };
4176
4224
 
4177
4225
  return /*#__PURE__*/external_react_default().createElement("div", {
@@ -4281,6 +4329,7 @@ const TicketTypeComponent = ({
4281
4329
  promoCode: promoCode,
4282
4330
  suggestedCode: promoState.suggestedCode,
4283
4331
  isAutoApplied: promoState.isAutoApplied,
4332
+ isCurrentTicketQualifying: promoState.isCodeValidForTicket(ticket),
4284
4333
  onInputChange: promoActions.onInputChange,
4285
4334
  onApply: handleApplyPromoCode,
4286
4335
  onRemove: promoActions.onRemove,
@@ -4886,14 +4935,18 @@ const RegistrationFormContent = _ref => {
4886
4935
  } = promo; // Error rendered in the promo notice slot — form-level warning layered on top
4887
4936
  // of the hook's own validation error (API rejection or status-derived).
4888
4937
 
4889
- const ticketStepError = unappliedCodeWarning ?? promoState.validationError; // Clear the unapplied-code warning once the condition that would have raised it
4890
- // no longer holds (input cleared, code applied, or a suggestion is showing).
4938
+ const ticketStepError = unappliedCodeWarning ?? promoState.validationError; // Clear the unapplied-code warning once it's no longer the right message to show.
4939
+ // The hook's own validationError takes precedence: if the user actually clicked
4940
+ // Apply and the backend rejected, surfacing "you didn't apply" would mask the
4941
+ // specific rejection reason. Depending on validationError (rather than on
4942
+ // applyPromoCode's brief Redux promoCode=truthy window) keeps the cleanup
4943
+ // robust against refactors of the apply action's sequencing.
4891
4944
 
4892
4945
  (0,external_react_.useEffect)(() => {
4893
- if (!formValues?.promoCode || promoCode || promoState.status === constants.PROMO_STATUS.SUGGESTED) {
4946
+ if (!formValues?.promoCode || promoCode || promoState.status === constants.PROMO_STATUS.SUGGESTED || promoState.validationError) {
4894
4947
  setUnappliedCodeWarning(null);
4895
4948
  }
4896
- }, [formValues?.promoCode, promoCode, promoState.status]);
4949
+ }, [formValues?.promoCode, promoCode, promoState.status, promoState.validationError]);
4897
4950
  const [ref, {
4898
4951
  height
4899
4952
  }] = (0,external_react_use_namespaceObject.useMeasure)();