summit-registration-lite 7.0.5 → 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/components/index.js +85 -32
- package/dist/components/index.js.map +1 -1
- package/dist/components/registration-form.js +85 -32
- package/dist/components/registration-form.js.map +1 -1
- package/dist/components/registration-modal.js +85 -32
- package/dist/components/registration-modal.js.map +1 -1
- package/dist/index.js +85 -32
- package/dist/index.js.map +1 -1
- package/e2e/promo-code-date-filter.spec.js +103 -0
- package/e2e/promo-code-discovery.spec.js +55 -7
- package/e2e/promo-code-invalid-clears-warning.spec.js +57 -0
- package/package.json +3 -3
- package/.claude/rules/summit-registration-lite-component-props.md +0 -95
- package/.claude/rules/summit-registration-lite-i18n.md +0 -62
- package/.claude/rules/summit-registration-lite-payment-providers.md +0 -69
- package/.claude/rules/summit-registration-lite-project.md +0 -80
- package/.claude/rules/summit-registration-lite-redux-actions.md +0 -65
- package/.claude/rules/summit-registration-lite-step-flow.md +0 -77
- package/.claude/rules/summit-registration-lite-testing.md +0 -97
- package/.claude/rules/summit-registration-lite-utils.md +0 -71
- package/.claude/skills/summit-registration-lite-add-provider/SKILL.md +0 -155
- package/.claude/skills/summit-registration-lite-dev-setup/SKILL.md +0 -67
- package/.claude/skills/summit-registration-lite-publish/SKILL.md +0 -64
- package/.claude/skills/summit-registration-lite-scaffold-component/SKILL.md +0 -152
- package/.codegraph/config.json +0 -141
- package/.codegraph/daemon.pid +0 -6
package/dist/components/index.js
CHANGED
|
@@ -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 `!
|
|
1490
|
-
//
|
|
1491
|
-
//
|
|
1492
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
1534
|
-
|
|
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,
|
|
1549
|
-
//
|
|
1550
|
-
//
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
3133
|
-
|
|
3134
|
-
|
|
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
|
-
|
|
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
|
|
3918
|
-
//
|
|
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
|
|