@riverbankcms/sdk 0.149.0 → 0.150.0
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/EventEmbeddedCheckoutStep-D00Kc8Zj.mjs +285 -0
- package/dist/{PreviewEditorSidebar-DBKln68M.mjs → PreviewEditorSidebar-7ZhmmnBB.mjs} +3 -3
- package/dist/{PreviewEditorUI-DpRX4Mum.mjs → PreviewEditorUI-BIJucTRd.mjs} +2 -2
- package/dist/{SdkPreviewModeRuntime-DJPmPY-r.mjs → SdkPreviewModeRuntime-tj45gmCX.mjs} +4 -3
- package/dist/{SiteChromeCustomizeContext-JYA0cJ_Z.mjs → SiteChromeCustomizeContext-BFOt0ss-.mjs} +4454 -4059
- package/dist/_dts/ai/src/contracts/proposals.d.ts +660 -0
- package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +510 -0
- package/dist/_dts/api/src/billing.d.ts +2 -2
- package/dist/_dts/api/src/contentRuntime.d.ts +5 -1
- package/dist/_dts/api/src/footerCompositionContracts.d.ts +9 -0
- package/dist/_dts/api/src/footerSettings.d.ts +3 -3
- package/dist/_dts/api/src/index.d.ts +1 -1
- package/dist/_dts/api/src/navigation/bookingPortalLink.d.ts +13 -0
- package/dist/_dts/api/src/navigation.d.ts +1 -0
- package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +9 -173
- package/dist/_dts/api/src/sdkThemeWire.d.ts +12 -0
- package/dist/_dts/api/src/siteManagementEndpoints.d.ts +3 -3
- package/dist/_dts/api/src/siteSettings.d.ts +2 -0
- package/dist/_dts/blocks/src/client/site-chrome.d.ts +1 -1
- package/dist/_dts/blocks/src/client.d.ts +2 -1
- package/dist/_dts/blocks/src/rendering/server.d.ts +1 -1
- package/dist/_dts/blocks/src/system/blocks/site-footer-runtime.d.ts +30 -3
- package/dist/_dts/blocks/src/system/designCapabilities.d.ts +10 -1
- package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
- package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +38 -18
- package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessEventRegistrationFlow.d.ts +1 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.server.d.ts +6 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventEmbeddedCheckoutStep.d.ts +13 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationPayment.d.ts +15 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationWizardActions.d.ts +0 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/FullyBookedStep.d.ts +6 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentBlockingPanel.d.ts +3 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentSelectionStep.d.ts +2 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +15 -5
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +4 -2
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/paymentLocation.d.ts +3 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/paymentResponse.d.ts +72 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +66 -5
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +1 -4
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationStateMachine.d.ts +13 -5
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/sessionStorage.d.ts +11 -52
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +19 -2
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +7 -42
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +7 -8
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +90 -24
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useGuestRegistrationRecovery.d.ts +30 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.d.ts +24 -12
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useRegistrationAdmission.d.ts +18 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/wizardSteps.d.ts +6 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +1 -1
- package/dist/_dts/blocks/src/system/runtime/shared/stripe/browserStripe.d.ts +8 -0
- package/dist/_dts/blocks/src/system/transforms/registry/eventCalendar.d.ts +8 -0
- package/dist/_dts/commerce/src/purchase/checkoutCopy.d.ts +10 -0
- package/dist/_dts/commerce/src/purchase/checkoutPresentation.d.ts +49 -0
- package/dist/_dts/commerce/src/purchase/checkoutSessionMetadata.d.ts +4 -4
- package/dist/_dts/commerce/src/purchase/checkoutStripeFinalizationPlanning.d.ts +5 -1
- package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +9 -0
- package/dist/_dts/commerce/src/purchase/index.d.ts +2 -0
- package/dist/_dts/commerce/src/purchase/orderItemMetadata.d.ts +1 -0
- package/dist/_dts/commerce/src/purchase/stripeCheckoutReturnUrl.d.ts +9 -2
- package/dist/_dts/commerce/src/purchase/types.d.ts +5 -6
- package/dist/_dts/content-editor/src/FooterComposerOverview.d.ts +3 -2
- package/dist/_dts/content-editor/src/footerPreviewRuntime.d.ts +6 -1
- package/dist/_dts/content-editor/src/useFooterComposerState.d.ts +7 -0
- package/dist/_dts/core/src/index.d.ts +1 -0
- package/dist/_dts/core/src/patch/index.d.ts +30 -0
- package/dist/_dts/db/src/generated/supabase/database.types.d.ts +30 -1
- package/dist/_dts/sdk/src/bookings/index.d.ts +2 -1
- package/dist/_dts/sdk/src/bookings/types.d.ts +1 -1
- package/dist/_dts/sdk/src/version.d.ts +1 -1
- package/dist/_dts/theme-core/src/blocks/index.d.ts +1 -0
- package/dist/_dts/theme-core/src/blocks/opaquePanels.d.ts +147 -0
- package/dist/_dts/theme-core/src/boxBackgroundSchema.d.ts +22 -0
- package/dist/_dts/theme-core/src/buttons/buttonSurfaceContext.d.ts +22 -0
- package/dist/_dts/theme-core/src/cards/roles.d.ts +12 -0
- package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
- package/dist/_dts/theme-core/src/mock-themes/all.d.ts +198 -0
- package/dist/_dts/theme-core/src/palette/utils/index.d.ts +2 -0
- package/dist/_dts/theme-core/src/palette/utils/scrimContrast.d.ts +22 -0
- package/dist/_dts/theme-core/src/runtime/bridgeColourTokens.d.ts +10 -0
- package/dist/_dts/theme-core/src/schema.d.ts +330 -0
- package/dist/_dts/theme-core/src/site-styles/appearancePresetCatalog.d.ts +0 -51
- package/dist/_dts/theme-core/src/site-styles/blockAppearancePresets.d.ts +0 -51
- package/dist/_dts/theme-core/src/site-styles/lookResolution.d.ts +11 -0
- package/dist/_dts/theme-core/src/site-styles/pageDesignEditorCapabilities.d.ts +7 -1
- package/dist/_dts/theme-core/src/site-styles/pageDesignEditorModel.d.ts +7 -1
- package/dist/_dts/theme-core/src/site-styles/previewRuntimeCapabilities.d.ts +12 -6
- package/dist/_dts/theme-core/src/site-styles/runtimeCompiler.d.ts +7 -4
- package/dist/_dts/theme-core/src/site-styles/sectionSurfaceBackgrounds.d.ts +65 -0
- package/dist/_dts/theme-core/src/site-styles/types.d.ts +17 -0
- package/dist/_dts/theme-core/src/surfaces/opaqueSurface.d.ts +43 -0
- package/dist/client/{BookingMailingListNote-Cr3EeTP5.mjs → BookingMailingListNote-CYHOufsI.mjs} +10 -440
- package/dist/client/{CheckboxChoice-B33MqoUo.mjs → CheckboxChoice-M-3HycLw.mjs} +1 -1
- package/dist/client/{DemoBookingPortalAccess-BFGB1Fay.mjs → DemoBookingPortalAccess-Cvr9O_ca.mjs} +2 -2
- package/dist/client/{EventCombined.client-DKYV0_ft.mjs → EventCombined.client-m5UZIobP.mjs} +7 -7
- package/dist/client/EventEmbeddedCheckoutStep-DL6zFAkA.mjs +289 -0
- package/dist/client/{EventListing.client-B9UIcEYc.mjs → EventListing.client-DeMfCM04.mjs} +2 -2
- package/dist/client/{LeafletStaticMap-CFLPkP0K.mjs → LeafletStaticMap-CvZ3gOdD.mjs} +1 -1
- package/dist/client/{checkoutUrls-CXhko2Rv.mjs → RichText-BtfNvQV6.mjs} +2 -29
- package/dist/client/{TerminalPanel-JjmgxEsR.mjs → TerminalPanel-CUCNEwlH.mjs} +2 -18
- package/dist/client/{ThemedButton-CKV1Jh2m.mjs → ThemedButton-C7WhXuRl.mjs} +2 -2
- package/dist/client/{accordion-DXWIfT3T.mjs → accordion-cQm5mjqs.mjs} +1 -1
- package/dist/client/{bookingForm-CjZgHNvv.mjs → bookingForm-BVPJE5RQ.mjs} +11 -10
- package/dist/client/bookings.mjs +2668 -1134
- package/dist/client/{carousel-DSLikmvZ.mjs → carousel-3vgnY7LC.mjs} +2 -2
- package/dist/client/{carousel.client-A2lG-9od.mjs → carousel.client-CISsdZvg.mjs} +1 -1
- package/dist/client/{cart-HDjWReFt.mjs → cart-BGK4oOjm.mjs} +3 -3
- package/dist/client/{checkout-CUvRZA3W.mjs → checkout-GsBLUNJD.mjs} +3 -3
- package/dist/client/checkoutUrls-ChxwaNtO.mjs +28 -0
- package/dist/client/client.mjs +3042 -1868
- package/dist/client/{colorStyles-j5U9v5JA.mjs → colorStyles-D-32xHZh.mjs} +2 -30
- package/dist/client/{context-DFmaZa-Q.mjs → context-D_8s12Fp.mjs} +1 -1
- package/dist/client/{dates-BBep1JnF.mjs → dates-DeJQbQ9T.mjs} +2 -2
- package/dist/client/{eventCalendar-zEobhxo4.mjs → eventCalendar-cmucD4NR.mjs} +10 -10
- package/dist/client/{eventCombined-Dp7bz5k-.mjs → eventCombined-ku3E9FTl.mjs} +4 -4
- package/dist/client/{eventDetails-FE-zqTt7.mjs → eventDetails-CyKZ61sF.mjs} +5 -5
- package/dist/client/{eventListing-BMt5snal.mjs → eventListing-B7yxGIhX.mjs} +4 -4
- package/dist/client/{eventRegistration-O6ZxHfOb.mjs → eventRegistration-BoXfSFgZ.mjs} +1403 -857
- package/dist/client/{form-DFa1D8vt.mjs → form-CNQOWhJo.mjs} +5 -5
- package/dist/client/{headerSection-CmjHLR-N.mjs → headerSection-DahaBJGQ.mjs} +3 -3
- package/dist/client/hooks.mjs +123 -64
- package/dist/client/{locationMap-BXKP_lGh.mjs → locationMap-BoDjqYlW.mjs} +4 -4
- package/dist/client/{media-Cl38iOMI.mjs → media-BFY0y2rb.mjs} +2 -1
- package/dist/client/{newsletterForm-CU-HmxGx.mjs → newsletterForm-DtzXDklw.mjs} +4 -4
- package/dist/client/{pricing-KEAEsibq.mjs → pricing-DXEh2Lhh.mjs} +1 -1
- package/dist/client/{productDetail-Ckp7MGnt.mjs → productDetail-D7cPa-00.mjs} +3 -3
- package/dist/client/{productList-D91V0sYa.mjs → productList-D1XMap66.mjs} +3 -3
- package/dist/client/{propsDecoder-DZd6RJeA.mjs → propsDecoder-DiIeBopB.mjs} +758 -245
- package/dist/client/rendering/client.mjs +4381 -2892
- package/dist/client/rendering/islands.mjs +19 -19
- package/dist/client/rendering.mjs +3026 -1852
- package/dist/client/result-DRW7JWXz.mjs +30 -0
- package/dist/client/{routeIslandApiConfig-E1EkhhYY.mjs → routeIslandApiConfig-wzR4lKAY.mjs} +1 -1
- package/dist/client/scrollToBlock-C86BYkyV.mjs +17 -0
- package/dist/client/{shop-4F2jLQPI.mjs → shop-CYT5P-S5.mjs} +8 -8
- package/dist/client/{shop-commerce-store-vicweP2K.mjs → shop-commerce-store-wKhsJQxI.mjs} +1 -1
- package/dist/client/{shop-commerce-BbfCJjB3.mjs → shop-commerce-ypBu_dx4.mjs} +9 -9
- package/dist/client/{shopShared-DlONJt0T.mjs → shopShared-DFGhYrf2.mjs} +52 -6
- package/dist/client/{useEventFilters-COYyL3m7.mjs → useEventFilters-blSbN5o7.mjs} +8 -8
- package/dist/preview-next/client/runtime.mjs +3 -3
- package/dist/{sdk-runtime-CwvFFjB-.mjs → sdk-runtime-2p8uQk1V.mjs} +1 -1
- package/dist/{separator-ByL72zUT.mjs → separator-Cvvzk0Oo.mjs} +1 -1
- package/dist/server/components.mjs +4066 -3765
- package/dist/server/config-validation.mjs +185 -126
- package/dist/server/config.mjs +185 -126
- package/dist/server/data.mjs +123 -64
- package/dist/server/index.mjs +1 -1
- package/dist/server/next.mjs +615 -314
- package/dist/server/page-converter.mjs +100 -56
- package/dist/server/prebuild.mjs +1 -1
- package/dist/server/rendering/server.mjs +4041 -3751
- package/dist/server/rendering.mjs +4069 -3768
- package/dist/server/routing.mjs +149 -72
- package/dist/server/server.mjs +124 -65
- package/dist/server/theme-bridge.mjs +1122 -310
- package/dist/server/theme.mjs +126 -116
- package/dist/spinner-CDo6AkDz.mjs +658 -0
- package/dist/styles/index.css +75 -212
- package/package.json +1 -1
- package/dist/_dts/billing/src/checkout/platform.d.ts +0 -77
- package/dist/_dts/billing/src/config/env.d.ts +0 -29
- package/dist/_dts/billing/src/stripe/client.d.ts +0 -29
- package/dist/_dts/blocks/src/system/runtime/hooks/usePaymentVerification.d.ts +0 -63
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventCheckoutHandoffRestore.d.ts +0 -9
- package/dist/client/urlParams-D71VXFGx.mjs +0 -47
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { a as buildBookingPaymentSelectionViewModel, c as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { a as buildBookingPaymentSelectionViewModel, c as useBookingNowIso, i as usePolling, n as BookingConsentAgreement, o as resolveBookingPaymentDueNowCents, r as StepIndicator$1, s as formatPrice, t as BookingMailingListNote } from "./BookingMailingListNote-CYHOufsI.mjs";
|
|
3
|
+
import { $t as parseEventRegistrationErrorReason, An as publicEventPricingOptions, C as resolveDiscountContext, D as getRegistrationStepPresentation, E as toRegistrationRequestSubject, M as resolveEventRegistrationScopedEvents, Mn as publicEventPricingType, Nn as formatCurrencyAmount, O as getStepNumber, On as publicEventPricingCurrency, Ot as getRegistrationSuccessCopy, Qt as eventRegistrationErrors, S as findPreSelectedEvent, Sn as toScheduledBookingDateTime, T as resolveRegistrationTarget, Wt as assertNever, _ as buildEventRegistrationDisplayFromScopedEvents, b as toWholeSeriesRegistrationEvents, bn as toBookingTimeZone, cn as resolveBookingSignupOffer, en as getStripeMinimumChargeRequirement, fn as formatBookingDateTime, ft as groupPublicEventsByRun, jn as publicEventPricingStrategy, k as getWizardStepsForContext, mn as formatBookingTime, nn as planFlexibleBalanceCreditFirst, t as defineIslandPropsDecoder, tn as planCheckoutPaymentFlow, un as formatBookingDate, v as getRegistrationStartAt, w as resolveEventRequiresPayment, x as findEventByOccurrenceId, y as toRegistrationEvents } from "./propsDecoder-DiIeBopB.mjs";
|
|
4
|
+
import { i as SpinnerNode, n as ThemedButtonLink, t as ThemedButton } from "./ThemedButton-C7WhXuRl.mjs";
|
|
5
|
+
import { a as canJoinEventWaitlistOnAnyMode, c as formatTicketTypeRemaining, f as getRemainingPlaces, h as selectMostOpenCapacitySource, i as canJoinEventWaitlist, m as selectEventCapacitySource, o as formatEventRemainingPlaces, p as isEventFull, r as formatEventPriceDisplay, s as formatRemainingPlaces, u as getEventAvailability } from "./pricing-DXEh2Lhh.mjs";
|
|
6
|
+
import { n as useBlockApi, r as BlockApiError, t as BlockApiProvider } from "./context-D_8s12Fp.mjs";
|
|
6
7
|
import { n as isSpamProtectionEnabled, r as AltchaWidget, t as getChallengeUrl } from "./src-B833rYRr.mjs";
|
|
7
|
-
import { a as requestDemoPortalMagicLink, f as storeDemoBookingPass, i as buildPortalCallbackUrl, o as DiscountCodeDisclosure, r as CUSTOMER_PORTAL_PATH, t as BookingConfirmation } from "./DemoBookingPortalAccess-
|
|
8
|
-
import {
|
|
9
|
-
import { i as SpinnerNode, n as ThemedButtonLink, t as ThemedButton } from "./ThemedButton-CKV1Jh2m.mjs";
|
|
8
|
+
import { a as requestDemoPortalMagicLink, f as storeDemoBookingPass, i as buildPortalCallbackUrl, o as DiscountCodeDisclosure, r as CUSTOMER_PORTAL_PATH, t as BookingConfirmation } from "./DemoBookingPortalAccess-Cvr9O_ca.mjs";
|
|
9
|
+
import { i as useDiscountCodeUi, n as StateIcon, r as CheckIcon, t as TerminalPanel } from "./TerminalPanel-CUCNEwlH.mjs";
|
|
10
10
|
import { t as FeedbackMessage } from "./FeedbackMessage-CtLvvS_d.mjs";
|
|
11
11
|
import { i as getFieldValidationControlProps, r as FieldValidationMessage } from "./mailingListSignupCopy-DZSHZrm6.mjs";
|
|
12
|
-
import { t as
|
|
13
|
-
import { t as CheckboxChoice } from "./CheckboxChoice-B33MqoUo.mjs";
|
|
12
|
+
import { t as CheckboxChoice } from "./CheckboxChoice-M-3HycLw.mjs";
|
|
14
13
|
import { t as createBodyThemePortalRoot } from "./themePortalRoot-AGs4cE7t.mjs";
|
|
15
|
-
import { n as consumeSearchParams } from "./urlParams-D71VXFGx.mjs";
|
|
16
14
|
import { t as CrossIcon } from "./CrossIcon-Dn1KRKW5.mjs";
|
|
17
15
|
import { t as mountReactIsland } from "./reactMount-SY8AVRof.mjs";
|
|
18
|
-
import { t as buildRouteIslandApiConfig } from "./routeIslandApiConfig-
|
|
16
|
+
import { t as buildRouteIslandApiConfig } from "./routeIslandApiConfig-wzR4lKAY.mjs";
|
|
19
17
|
import { t as createLucideIcon } from "./createLucideIcon-PaLVMov4.mjs";
|
|
20
18
|
import { t as ModalShell } from "./ModalShell-CqkBvBIC.mjs";
|
|
21
|
-
import React, { useCallback, useEffect, useId, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
19
|
+
import React, { Suspense, lazy, useCallback, useEffect, useId, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
20
|
+
import { z } from "zod";
|
|
22
21
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
23
22
|
import { createPortal } from "react-dom";
|
|
24
23
|
//#region ../core/src/uuid.ts
|
|
@@ -175,6 +174,161 @@ const Wallet = createLucideIcon("wallet", [["path", {
|
|
|
175
174
|
key: "xoc0q4"
|
|
176
175
|
}]]);
|
|
177
176
|
//#endregion
|
|
177
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/registrationCompletion.ts
|
|
178
|
+
/** One admission decision for navigation, labels and completion. Email recovery
|
|
179
|
+
* permits navigation only; submission remains the owner of payment recovery. */
|
|
180
|
+
function resolveRegistrationAdmission(input) {
|
|
181
|
+
if (input.hasWaitlistClaim) return { kind: "resume" };
|
|
182
|
+
if (input.eligibilityLoading) return { kind: "checking" };
|
|
183
|
+
if (input.eligibilityError) return {
|
|
184
|
+
kind: "closed",
|
|
185
|
+
reason: "optionsUnavailable",
|
|
186
|
+
message: input.eligibilityError
|
|
187
|
+
};
|
|
188
|
+
if (!input.eligibility) return { kind: "checking" };
|
|
189
|
+
const { registration, availability } = input.eligibility;
|
|
190
|
+
if (registration.kind === "pending_payment") return { kind: "resume" };
|
|
191
|
+
if (registration.kind === "waitlisted") return {
|
|
192
|
+
kind: "closed",
|
|
193
|
+
reason: "alreadyWaitlisted",
|
|
194
|
+
message: "You're already on the waitlist for this event."
|
|
195
|
+
};
|
|
196
|
+
return admissionFromAvailability(availability, input.isAuthenticated);
|
|
197
|
+
}
|
|
198
|
+
function admissionFromAvailability(availability, isAuthenticated) {
|
|
199
|
+
switch (availability.kind) {
|
|
200
|
+
case "available": return { kind: "open" };
|
|
201
|
+
case "waitlist": return {
|
|
202
|
+
kind: "waitlist",
|
|
203
|
+
recovery: isAuthenticated ? "none" : "byEmail"
|
|
204
|
+
};
|
|
205
|
+
case "unavailable": switch (availability.reason) {
|
|
206
|
+
case "full": return {
|
|
207
|
+
kind: "fullyBooked",
|
|
208
|
+
recovery: isAuthenticated ? "none" : "byEmail"
|
|
209
|
+
};
|
|
210
|
+
case "paymentUnavailable": return {
|
|
211
|
+
kind: "closed",
|
|
212
|
+
reason: "unavailable",
|
|
213
|
+
message: "Payment options are not available for this event."
|
|
214
|
+
};
|
|
215
|
+
case "selectionRequired": return { kind: "open" };
|
|
216
|
+
case "bookingClosed": return {
|
|
217
|
+
kind: "closed",
|
|
218
|
+
reason: "unavailable",
|
|
219
|
+
message: "Registration is not available for this event."
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function stepAfterTickets(admission) {
|
|
225
|
+
switch (admission.kind) {
|
|
226
|
+
case "open":
|
|
227
|
+
case "resume":
|
|
228
|
+
case "waitlist": return "enter-details";
|
|
229
|
+
case "fullyBooked": return "fully-booked";
|
|
230
|
+
case "checking":
|
|
231
|
+
case "closed": return "select-tickets";
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function resolveRegistrationAvailabilityBlocker(admission) {
|
|
235
|
+
switch (admission.kind) {
|
|
236
|
+
case "open":
|
|
237
|
+
case "resume":
|
|
238
|
+
case "waitlist": return null;
|
|
239
|
+
case "fullyBooked": return admission.recovery === "none" ? {
|
|
240
|
+
kind: "unavailable",
|
|
241
|
+
message: "This event is fully booked."
|
|
242
|
+
} : null;
|
|
243
|
+
case "checking": return {
|
|
244
|
+
kind: "pending",
|
|
245
|
+
message: "Checking availability..."
|
|
246
|
+
};
|
|
247
|
+
case "closed": return {
|
|
248
|
+
kind: "unavailable",
|
|
249
|
+
message: admission.message
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function resolvePricingSelectionBlocker(input) {
|
|
254
|
+
if (input.selectionRequired && !input.hasValidSelection) return "Choose at least one available ticket to continue.";
|
|
255
|
+
return input.slidingScaleSelectionMessage;
|
|
256
|
+
}
|
|
257
|
+
/** Submit owns fresh admission. Open quotes do not depend on an email lookup. */
|
|
258
|
+
function resolveGuestNextStepCheck(admission, request) {
|
|
259
|
+
switch (admission.kind) {
|
|
260
|
+
case "open":
|
|
261
|
+
case "resume": return {
|
|
262
|
+
kind: "skip",
|
|
263
|
+
outcome: { kind: "continue" }
|
|
264
|
+
};
|
|
265
|
+
case "checking": return {
|
|
266
|
+
kind: "blocked",
|
|
267
|
+
message: "Checking availability..."
|
|
268
|
+
};
|
|
269
|
+
case "closed": return {
|
|
270
|
+
kind: "blocked",
|
|
271
|
+
message: admission.message
|
|
272
|
+
};
|
|
273
|
+
case "fullyBooked":
|
|
274
|
+
if (admission.recovery === "none") return {
|
|
275
|
+
kind: "blocked",
|
|
276
|
+
message: "This event is fully booked."
|
|
277
|
+
};
|
|
278
|
+
return recoveryCheckRequest(request);
|
|
279
|
+
case "waitlist": return admission.recovery === "none" ? {
|
|
280
|
+
kind: "skip",
|
|
281
|
+
outcome: { kind: "waitlist" }
|
|
282
|
+
} : recoveryCheckRequest(request);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function recoveryCheckRequest(request) {
|
|
286
|
+
return request ? {
|
|
287
|
+
kind: "check",
|
|
288
|
+
request
|
|
289
|
+
} : {
|
|
290
|
+
kind: "blocked",
|
|
291
|
+
message: "Choose an event before checking your booking."
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function resolveRegistrationTicketContinuation(input) {
|
|
295
|
+
if (input.isCheckingExistingRegistration) return {
|
|
296
|
+
kind: "blocked",
|
|
297
|
+
label: "Checking registration...",
|
|
298
|
+
pending: true,
|
|
299
|
+
message: "Your booking options are being checked."
|
|
300
|
+
};
|
|
301
|
+
if (input.ticketSelectionRequired && !input.hasValidTicketSelection) return {
|
|
302
|
+
kind: "blocked",
|
|
303
|
+
label: "Choose a ticket",
|
|
304
|
+
pending: false,
|
|
305
|
+
message: "Choose at least one available ticket to continue."
|
|
306
|
+
};
|
|
307
|
+
switch (input.admission.kind) {
|
|
308
|
+
case "checking": return {
|
|
309
|
+
kind: "blocked",
|
|
310
|
+
label: "Checking availability...",
|
|
311
|
+
pending: true,
|
|
312
|
+
message: "Your booking options are being checked."
|
|
313
|
+
};
|
|
314
|
+
case "closed": return {
|
|
315
|
+
kind: "blocked",
|
|
316
|
+
label: input.admission.reason === "alreadyWaitlisted" ? "Already on waitlist" : input.admission.reason === "optionsUnavailable" ? "Availability unavailable" : "Registration unavailable",
|
|
317
|
+
pending: false,
|
|
318
|
+
message: input.admission.message
|
|
319
|
+
};
|
|
320
|
+
case "open":
|
|
321
|
+
case "resume":
|
|
322
|
+
case "waitlist":
|
|
323
|
+
case "fullyBooked": return {
|
|
324
|
+
kind: "advance",
|
|
325
|
+
admission: input.admission,
|
|
326
|
+
label: "Continue",
|
|
327
|
+
pending: false
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
178
332
|
//#region ../blocks/src/system/runtime/nodes/event-registration/eventPaymentOptions.ts
|
|
179
333
|
function getApprovedDirectPaymentCollectionOptionKinds(paymentOptions) {
|
|
180
334
|
return paymentOptions.flatMap((option) => {
|
|
@@ -274,9 +428,6 @@ function resolveEventRegistrationDirectPaymentSelection(input) {
|
|
|
274
428
|
}
|
|
275
429
|
//#endregion
|
|
276
430
|
//#region ../blocks/src/system/runtime/nodes/event-registration/registrationStateMachine.ts
|
|
277
|
-
function hasBlockingExistingRegistration(input) {
|
|
278
|
-
return !input.hasWaitlistClaim && input.isAuthenticated && input.registration.kind === "waitlisted";
|
|
279
|
-
}
|
|
280
431
|
function createInitialRegistrationFlowState(hasPreSelectedContext) {
|
|
281
432
|
return {
|
|
282
433
|
step: hasPreSelectedContext ? "select-tickets" : "select-event",
|
|
@@ -352,8 +503,7 @@ function isSelectionValid(input) {
|
|
|
352
503
|
function normalizeRegistrationFlowState(state) {
|
|
353
504
|
const hasPass = Boolean(state.eligibility?.eligiblePasses.length);
|
|
354
505
|
const hasMembership = Boolean(state.eligibility?.eligibleMemberships.length);
|
|
355
|
-
|
|
356
|
-
if (nextPassOptOut && (state.step === "select-tickets" || state.ticketsCount !== 1 || !hasPass || hasMembership)) nextPassOptOut = false;
|
|
506
|
+
const nextPassOptOut = state.passOptOut && !(state.step === "select-tickets" || state.ticketsCount !== 1 || !hasPass || hasMembership);
|
|
357
507
|
const autoSelection = computeAutoSelection({
|
|
358
508
|
isAuthenticated: state.isAuthenticated,
|
|
359
509
|
eligibility: state.eligibility,
|
|
@@ -378,8 +528,27 @@ function normalizeRegistrationFlowState(state) {
|
|
|
378
528
|
};
|
|
379
529
|
}
|
|
380
530
|
function registrationFlowReducer(state, action) {
|
|
531
|
+
if ([
|
|
532
|
+
"payment",
|
|
533
|
+
"submitting",
|
|
534
|
+
"verifying-payment",
|
|
535
|
+
"verifying-timeout",
|
|
536
|
+
"payment-status"
|
|
537
|
+
].includes(state.step) && action.type !== "set-step") return state;
|
|
381
538
|
switch (action.type) {
|
|
382
|
-
case "
|
|
539
|
+
case "admission-updated":
|
|
540
|
+
case "tickets-completed":
|
|
541
|
+
case "recovery-checked": return reduceRegistrationAdmission(state, action);
|
|
542
|
+
case "set-step": return [
|
|
543
|
+
"payment",
|
|
544
|
+
"submitting",
|
|
545
|
+
"verifying-payment",
|
|
546
|
+
"verifying-timeout",
|
|
547
|
+
"payment-status"
|
|
548
|
+
].includes(action.step) ? {
|
|
549
|
+
...state,
|
|
550
|
+
step: action.step
|
|
551
|
+
} : normalizeRegistrationFlowState({
|
|
383
552
|
...state,
|
|
384
553
|
step: action.step
|
|
385
554
|
});
|
|
@@ -395,18 +564,8 @@ function registrationFlowReducer(state, action) {
|
|
|
395
564
|
...state,
|
|
396
565
|
eligibility: action.eligibility
|
|
397
566
|
});
|
|
398
|
-
case "set-manual-payment-selection":
|
|
399
|
-
|
|
400
|
-
paymentSelection: normalizePaymentSelection(action.selection),
|
|
401
|
-
selectionSource: "manual",
|
|
402
|
-
passOptOut: action.selection.method === "stripe" ? true : state.passOptOut
|
|
403
|
-
});
|
|
404
|
-
case "use-card-instead-of-pass": return normalizeRegistrationFlowState({
|
|
405
|
-
...state,
|
|
406
|
-
passOptOut: true,
|
|
407
|
-
paymentSelection: computeDirectPaymentSelection(state.eligibility),
|
|
408
|
-
selectionSource: "manual"
|
|
409
|
-
});
|
|
567
|
+
case "set-manual-payment-selection":
|
|
568
|
+
case "use-card-instead-of-pass": return reduceRegistrationPaymentChoice(state, action);
|
|
410
569
|
case "event-changed": return normalizeRegistrationFlowState({
|
|
411
570
|
...state,
|
|
412
571
|
step: action.hasPreSelectedContext ? "select-tickets" : "select-event",
|
|
@@ -416,7 +575,6 @@ function registrationFlowReducer(state, action) {
|
|
|
416
575
|
eligibility: null
|
|
417
576
|
});
|
|
418
577
|
case "reset": return createInitialRegistrationFlowState(action.hasPreSelectedContext);
|
|
419
|
-
default: return state;
|
|
420
578
|
}
|
|
421
579
|
}
|
|
422
580
|
function isEntitlementSelection(selection) {
|
|
@@ -426,6 +584,38 @@ function requiresPaymentStep(params) {
|
|
|
426
584
|
if (!params.eventRequiresPayment) return false;
|
|
427
585
|
return !isEntitlementSelection(params.paymentSelection);
|
|
428
586
|
}
|
|
587
|
+
function reduceRegistrationPaymentChoice(state, action) {
|
|
588
|
+
switch (action.type) {
|
|
589
|
+
case "set-manual-payment-selection": return normalizeRegistrationFlowState({
|
|
590
|
+
...state,
|
|
591
|
+
paymentSelection: normalizePaymentSelection(action.selection),
|
|
592
|
+
selectionSource: "manual",
|
|
593
|
+
passOptOut: action.selection.method === "stripe" ? true : state.passOptOut
|
|
594
|
+
});
|
|
595
|
+
case "use-card-instead-of-pass": return normalizeRegistrationFlowState({
|
|
596
|
+
...state,
|
|
597
|
+
passOptOut: true,
|
|
598
|
+
paymentSelection: computeDirectPaymentSelection(state.eligibility),
|
|
599
|
+
selectionSource: "manual"
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function reduceRegistrationAdmission(state, action) {
|
|
604
|
+
switch (action.type) {
|
|
605
|
+
case "admission-updated": return state.step === "fully-booked" && action.admission.kind !== "checking" ? normalizeRegistrationFlowState({
|
|
606
|
+
...state,
|
|
607
|
+
step: stepAfterTickets(action.admission)
|
|
608
|
+
}) : state;
|
|
609
|
+
case "tickets-completed": return state.step === "select-tickets" ? normalizeRegistrationFlowState({
|
|
610
|
+
...state,
|
|
611
|
+
step: stepAfterTickets(action.admission)
|
|
612
|
+
}) : state;
|
|
613
|
+
case "recovery-checked": return state.step === "fully-booked" && action.outcome.kind !== "unavailable" ? normalizeRegistrationFlowState({
|
|
614
|
+
...state,
|
|
615
|
+
step: "enter-details"
|
|
616
|
+
}) : state;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
429
619
|
//#endregion
|
|
430
620
|
//#region ../blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.ts
|
|
431
621
|
function isEventRegistrationAvailable(event) {
|
|
@@ -767,16 +957,66 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
|
|
|
767
957
|
});
|
|
768
958
|
};
|
|
769
959
|
//#endregion
|
|
960
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/useRegistrationAdmission.ts
|
|
961
|
+
/** Owns admission and its navigation commands; renderers receive decisions. */
|
|
962
|
+
function useRegistrationAdmission(input) {
|
|
963
|
+
const { eligibility, eligibilityError, eligibilityLoading, hasWaitlistClaim, isAuthenticated, dispatchFlow } = input;
|
|
964
|
+
const admission = useMemo(() => resolveRegistrationAdmission({
|
|
965
|
+
eligibility,
|
|
966
|
+
eligibilityError,
|
|
967
|
+
eligibilityLoading,
|
|
968
|
+
hasWaitlistClaim,
|
|
969
|
+
isAuthenticated
|
|
970
|
+
}), [
|
|
971
|
+
eligibility,
|
|
972
|
+
eligibilityError,
|
|
973
|
+
eligibilityLoading,
|
|
974
|
+
hasWaitlistClaim,
|
|
975
|
+
isAuthenticated
|
|
976
|
+
]);
|
|
977
|
+
useEffect(() => {
|
|
978
|
+
dispatchFlow({
|
|
979
|
+
type: "admission-updated",
|
|
980
|
+
admission
|
|
981
|
+
});
|
|
982
|
+
}, [admission, dispatchFlow]);
|
|
983
|
+
const ticketContinuation = resolveRegistrationTicketContinuation({
|
|
984
|
+
admission,
|
|
985
|
+
ticketSelectionRequired: input.ticketSelectionRequired,
|
|
986
|
+
hasValidTicketSelection: input.hasValidTicketSelection,
|
|
987
|
+
isCheckingExistingRegistration: isAuthenticated && input.existingRegistrationLoading
|
|
988
|
+
});
|
|
989
|
+
const handleTicketsComplete = () => {
|
|
990
|
+
if (ticketContinuation.kind === "blocked") return {
|
|
991
|
+
ok: false,
|
|
992
|
+
reason: "blocked",
|
|
993
|
+
message: ticketContinuation.message
|
|
994
|
+
};
|
|
995
|
+
dispatchFlow({
|
|
996
|
+
type: "tickets-completed",
|
|
997
|
+
admission: ticketContinuation.admission
|
|
998
|
+
});
|
|
999
|
+
return { ok: true };
|
|
1000
|
+
};
|
|
1001
|
+
const onRecoveryChecked = (outcome) => dispatchFlow({
|
|
1002
|
+
type: "recovery-checked",
|
|
1003
|
+
outcome
|
|
1004
|
+
});
|
|
1005
|
+
return {
|
|
1006
|
+
admission,
|
|
1007
|
+
ticketContinuation,
|
|
1008
|
+
handleTicketsComplete,
|
|
1009
|
+
onRecoveryChecked
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
//#endregion
|
|
770
1013
|
//#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
|
|
771
1014
|
function resolveRegistrationRecoveryAction(step, action) {
|
|
772
1015
|
if (step === "payment-failed") return "payment-options";
|
|
773
1016
|
if (step === "payment-status") return "none";
|
|
774
|
-
if (step === "verifying-payment" || step === "verifying-timeout" || step === "
|
|
1017
|
+
if (step === "verifying-payment" || step === "verifying-timeout" || step === "payment") return "none";
|
|
775
1018
|
return action;
|
|
776
1019
|
}
|
|
777
|
-
function canRecoverToWaitlist(eligibility) {
|
|
778
|
-
return eligibility?.availability.kind === "waitlist" && eligibility.registration.kind !== "pending_payment";
|
|
779
|
-
}
|
|
780
1020
|
function shouldRefreshAfterRegistrationError(step, action, enabled) {
|
|
781
1021
|
return enabled || step === "error" && action === "places";
|
|
782
1022
|
}
|
|
@@ -790,7 +1030,6 @@ function resetRegistrationAfterExpiry(input) {
|
|
|
790
1030
|
const mode = input.preSelectedEvent?.deliveryMode;
|
|
791
1031
|
input.setSelectedAttendanceMode(mode === "online" || mode === "in_person" ? mode : null);
|
|
792
1032
|
input.setErrorMessage("");
|
|
793
|
-
input.setPendingRegistrationReference(null);
|
|
794
1033
|
}
|
|
795
1034
|
/** Retry the retained submission in place; explicit payment uncertainty stays status-only. */
|
|
796
1035
|
function recoverRegistrationError(action, handlers) {
|
|
@@ -1290,441 +1529,174 @@ function useResolvedWaitlistClaim(deps, credential) {
|
|
|
1290
1529
|
};
|
|
1291
1530
|
}
|
|
1292
1531
|
//#endregion
|
|
1293
|
-
//#region ../blocks/src/system/runtime/hooks/usePaymentVerification.ts
|
|
1294
|
-
/**
|
|
1295
|
-
* Hook for polling payment status after Stripe redirects.
|
|
1296
|
-
*
|
|
1297
|
-
* Built on top of the generic usePolling hook, this provides
|
|
1298
|
-
* a specialized interface for verifying event registration payments.
|
|
1299
|
-
*
|
|
1300
|
-
* @example
|
|
1301
|
-
* ```tsx
|
|
1302
|
-
* const verification = usePaymentVerification(
|
|
1303
|
-
* step === 'verifying-payment' && attendeeId
|
|
1304
|
-
* ? { transactionId: attendeeId }
|
|
1305
|
-
* : null
|
|
1306
|
-
* );
|
|
1307
|
-
*
|
|
1308
|
-
* useEffect(() => {
|
|
1309
|
-
* if (verification.status === 'completed') {
|
|
1310
|
-
* setStep('success');
|
|
1311
|
-
* } else if (verification.status === 'failed') {
|
|
1312
|
-
* setStep('payment-failed');
|
|
1313
|
-
* } else if (verification.status === 'timeout') {
|
|
1314
|
-
* setStep('verifying-timeout');
|
|
1315
|
-
* }
|
|
1316
|
-
* }, [verification.status]);
|
|
1317
|
-
* ```
|
|
1318
|
-
*/
|
|
1319
|
-
const paymentVerificationDeps = { useBlockApi };
|
|
1320
|
-
function resolvePaymentPollingTarget(config) {
|
|
1321
|
-
const reference = config.reference;
|
|
1322
|
-
return reference.scope === "occurrence" ? {
|
|
1323
|
-
key: `occurrence:${reference.attendeeId}`,
|
|
1324
|
-
reference
|
|
1325
|
-
} : {
|
|
1326
|
-
key: `wholeSeries:${reference.registrationId}`,
|
|
1327
|
-
reference
|
|
1328
|
-
};
|
|
1329
|
-
}
|
|
1330
|
-
function createPaymentPollingConfig(api, settings) {
|
|
1331
|
-
if (settings === null) return null;
|
|
1332
|
-
const target = resolvePaymentPollingTarget(settings);
|
|
1333
|
-
return {
|
|
1334
|
-
restartKey: target.key,
|
|
1335
|
-
pollFn: () => api.getEventRegistrationStatus(target.reference),
|
|
1336
|
-
isComplete: (result) => result.paymentStatus === "completed" || result.paymentStatus === "not_required",
|
|
1337
|
-
isFailed: (result) => result.paymentStatus === "failed" || result.paymentStatus === "refunded",
|
|
1338
|
-
maxAttempts: settings.maxAttempts,
|
|
1339
|
-
interval: settings.pollInterval
|
|
1340
|
-
};
|
|
1341
|
-
}
|
|
1342
|
-
function getPaymentVerificationError(polling, paymentStatus) {
|
|
1343
|
-
if (polling.status === "timeout") return "Could not verify payment. If you were charged, please contact support.";
|
|
1344
|
-
if (polling.status === "failed" && paymentStatus !== null) return paymentStatus === "refunded" ? "Payment was refunded. Please contact support." : "Payment was declined. Please try again.";
|
|
1345
|
-
return polling.error?.message ?? null;
|
|
1346
|
-
}
|
|
1347
|
-
function mapPaymentVerificationResult(polling) {
|
|
1348
|
-
const paymentStatus = polling.result?.paymentStatus ?? null;
|
|
1349
|
-
return {
|
|
1350
|
-
status: polling.status,
|
|
1351
|
-
paymentStatus,
|
|
1352
|
-
attendeeStatus: polling.result?.attendeeStatus ?? null,
|
|
1353
|
-
waitlistPosition: polling.result?.waitlistPosition ?? null,
|
|
1354
|
-
error: getPaymentVerificationError(polling, paymentStatus),
|
|
1355
|
-
retry: polling.retry
|
|
1356
|
-
};
|
|
1357
|
-
}
|
|
1358
|
-
/**
|
|
1359
|
-
* Hook for polling payment status after Stripe redirects.
|
|
1360
|
-
*
|
|
1361
|
-
* Pass config to start polling, or null to stop/idle.
|
|
1362
|
-
* The hook handles cleanup on unmount and starts a new cycle when the status
|
|
1363
|
-
* reference changes.
|
|
1364
|
-
*
|
|
1365
|
-
* @param config - Configuration for polling, or null to stop
|
|
1366
|
-
* @returns Verification result with status, data, and retry function
|
|
1367
|
-
*/
|
|
1368
|
-
function usePaymentVerification(config) {
|
|
1369
|
-
return usePaymentVerificationWithDeps(config, paymentVerificationDeps);
|
|
1370
|
-
}
|
|
1371
|
-
function usePaymentVerificationWithDeps(config, deps) {
|
|
1372
|
-
return mapPaymentVerificationResult(usePolling(createPaymentPollingConfig(deps.useBlockApi(), config)));
|
|
1373
|
-
}
|
|
1374
|
-
//#endregion
|
|
1375
|
-
//#region ../blocks/src/system/runtime/nodes/event-registration/constants.ts
|
|
1376
|
-
const EVENT_REGISTRATION_ANCHOR_ID = "event-registration";
|
|
1377
|
-
const EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM = "registration_attempt";
|
|
1378
|
-
//#endregion
|
|
1379
1532
|
//#region ../blocks/src/system/runtime/nodes/event-registration/sessionStorage.ts
|
|
1380
1533
|
const STORAGE_PREFIX = "riverbank:event-registration:";
|
|
1381
1534
|
const STALE_THRESHOLD_MS = 1440 * 60 * 1e3;
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1535
|
+
const paymentSelection = z.discriminatedUnion("method", [
|
|
1536
|
+
z.object({ method: z.literal("free") }),
|
|
1537
|
+
z.object({
|
|
1538
|
+
method: z.literal("stripe"),
|
|
1539
|
+
collectionOptionKind: z.enum([
|
|
1540
|
+
"upfront",
|
|
1541
|
+
"deferred_manual",
|
|
1542
|
+
"instalment_plan",
|
|
1543
|
+
"flexible_balance"
|
|
1544
|
+
])
|
|
1545
|
+
}),
|
|
1546
|
+
z.object({
|
|
1547
|
+
method: z.literal("pass"),
|
|
1548
|
+
passId: z.string(),
|
|
1549
|
+
productName: z.string().optional()
|
|
1550
|
+
}),
|
|
1551
|
+
z.object({
|
|
1552
|
+
method: z.literal("membership"),
|
|
1553
|
+
membershipId: z.string(),
|
|
1554
|
+
productName: z.string().optional()
|
|
1555
|
+
}),
|
|
1556
|
+
z.object({
|
|
1557
|
+
method: z.literal("pass_bundle"),
|
|
1558
|
+
bundlePassId: z.string(),
|
|
1559
|
+
productName: z.string().optional(),
|
|
1560
|
+
productDescription: z.string().optional(),
|
|
1561
|
+
productPrice: z.number().optional(),
|
|
1562
|
+
productCurrency: z.string().optional()
|
|
1563
|
+
}),
|
|
1564
|
+
z.object({
|
|
1565
|
+
method: z.literal("membership_bundle"),
|
|
1566
|
+
bundleMembershipId: z.string(),
|
|
1567
|
+
productName: z.string().optional(),
|
|
1568
|
+
productDescription: z.string().optional(),
|
|
1569
|
+
productPrice: z.number().optional(),
|
|
1570
|
+
productCurrency: z.string().optional(),
|
|
1571
|
+
productInterval: z.string().optional()
|
|
1572
|
+
})
|
|
1573
|
+
]);
|
|
1574
|
+
const schema = z.object({
|
|
1575
|
+
version: z.literal(6),
|
|
1576
|
+
reference: z.discriminatedUnion("scope", [z.object({
|
|
1577
|
+
scope: z.literal("occurrence"),
|
|
1578
|
+
attendeeId: z.string()
|
|
1579
|
+
}), z.object({
|
|
1580
|
+
scope: z.literal("wholeSeries"),
|
|
1581
|
+
registrationId: z.string()
|
|
1582
|
+
})]),
|
|
1583
|
+
formData: z.object({
|
|
1584
|
+
name: z.string(),
|
|
1585
|
+
email: z.string(),
|
|
1586
|
+
phone: z.string(),
|
|
1587
|
+
notes: z.string(),
|
|
1588
|
+
consentAccepted: z.boolean().optional(),
|
|
1589
|
+
mailingListOptIn: z.boolean().optional()
|
|
1590
|
+
}),
|
|
1591
|
+
ticketSelections: z.array(z.object({
|
|
1592
|
+
pricingOptionId: z.string().nullable(),
|
|
1593
|
+
quantity: z.number().int().positive()
|
|
1594
|
+
})).min(1),
|
|
1595
|
+
selectedEventId: z.string().nullable(),
|
|
1596
|
+
selectedAttendanceMode: z.enum(["in_person", "online"]).nullable().optional(),
|
|
1597
|
+
paymentSelection,
|
|
1598
|
+
guests: z.array(z.object({
|
|
1599
|
+
name: z.string(),
|
|
1600
|
+
email: z.string().nullable(),
|
|
1601
|
+
phone: z.string().nullable()
|
|
1602
|
+
})),
|
|
1603
|
+
selectedSlidingAmount: z.number().nullable(),
|
|
1604
|
+
discountCode: z.string(),
|
|
1605
|
+
mailingListSignupNote: z.enum(["confirmation_required", "not_confirmed"]).optional(),
|
|
1606
|
+
timestamp: z.number().finite()
|
|
1607
|
+
});
|
|
1608
|
+
function key(siteId, submissionId) {
|
|
1609
|
+
return `${STORAGE_PREFIX}${siteId}:${submissionId}`;
|
|
1610
|
+
}
|
|
1611
|
+
function savePendingRegistration(siteId, submissionId, data) {
|
|
1414
1612
|
try {
|
|
1415
|
-
const
|
|
1416
|
-
const ticketSelections = normalizeTicketSelectionLines(data.ticketSelections);
|
|
1417
|
-
if (ticketSelections.length === 0) {
|
|
1418
|
-
console.warn("[sessionStorage] No ticket selections to save, skipping registration state");
|
|
1419
|
-
return;
|
|
1420
|
-
}
|
|
1421
|
-
const entry = {
|
|
1613
|
+
const entry = schema.safeParse({
|
|
1422
1614
|
...data,
|
|
1423
|
-
ticketSelections,
|
|
1424
|
-
version:
|
|
1615
|
+
ticketSelections: normalizeTicketSelectionLines(data.ticketSelections),
|
|
1616
|
+
version: 6,
|
|
1425
1617
|
timestamp: Date.now()
|
|
1426
|
-
};
|
|
1427
|
-
sessionStorage.setItem(key, JSON.stringify(entry));
|
|
1428
|
-
} catch
|
|
1429
|
-
console.warn("[sessionStorage] Failed to save registration:", error);
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
/**
|
|
1433
|
-
* Get pending registration data after returning from Stripe.
|
|
1434
|
-
*
|
|
1435
|
-
* Returns null if:
|
|
1436
|
-
* - No entry exists for this checkout attempt
|
|
1437
|
-
* - Entry is stale (> 24 hours old)
|
|
1438
|
-
* - Entry is corrupt or wrong version
|
|
1439
|
-
* - Storage is unavailable
|
|
1440
|
-
*
|
|
1441
|
-
* @param siteId - Site ID
|
|
1442
|
-
* @param checkoutAttemptId - Correlation ID returned from checkout.
|
|
1443
|
-
* @returns Pending registration data or null
|
|
1444
|
-
*/
|
|
1445
|
-
function getPendingRegistration(siteId, checkoutAttemptId) {
|
|
1446
|
-
if (!isStorageAvailable()) return null;
|
|
1447
|
-
try {
|
|
1448
|
-
const key = getStorageKey(siteId, checkoutAttemptId);
|
|
1449
|
-
const stored = sessionStorage.getItem(key);
|
|
1450
|
-
if (stored) return parseAndValidateEntry(stored, key);
|
|
1451
|
-
return null;
|
|
1452
|
-
} catch (error) {
|
|
1453
|
-
console.warn("[sessionStorage] Failed to read registration:", error);
|
|
1454
|
-
return null;
|
|
1455
|
-
}
|
|
1618
|
+
});
|
|
1619
|
+
if (entry.success) sessionStorage.setItem(key(siteId, submissionId), JSON.stringify(entry.data));
|
|
1620
|
+
} catch {}
|
|
1456
1621
|
}
|
|
1457
|
-
|
|
1458
|
-
* Internal helper to parse and validate a storage entry.
|
|
1459
|
-
*/
|
|
1460
|
-
function parseAndValidateEntry(stored, key) {
|
|
1622
|
+
function getPendingRegistration(siteId, submissionId) {
|
|
1461
1623
|
try {
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
}
|
|
1467
|
-
const version = raw.version;
|
|
1468
|
-
if (version !== 4 && version !== 5) {
|
|
1469
|
-
console.warn("[sessionStorage] Outdated version, ignoring entry");
|
|
1470
|
-
sessionStorage.removeItem(key);
|
|
1471
|
-
return null;
|
|
1472
|
-
}
|
|
1473
|
-
const entry = normalizeStoredRegistration(raw);
|
|
1474
|
-
const timestamp = typeof entry.timestamp === "number" ? entry.timestamp : NaN;
|
|
1475
|
-
if (!Number.isFinite(timestamp)) {
|
|
1476
|
-
sessionStorage.removeItem(key);
|
|
1477
|
-
return null;
|
|
1478
|
-
}
|
|
1479
|
-
if (Date.now() - timestamp > STALE_THRESHOLD_MS) {
|
|
1480
|
-
console.info("[sessionStorage] Entry is stale, removing");
|
|
1481
|
-
sessionStorage.removeItem(key);
|
|
1482
|
-
return null;
|
|
1483
|
-
}
|
|
1484
|
-
const ticketSelections = normalizeTicketSelectionLines(entry.ticketSelections);
|
|
1485
|
-
if (ticketSelections.length === 0 || getTicketSelectionTotal(ticketSelections) <= 0) {
|
|
1486
|
-
sessionStorage.removeItem(key);
|
|
1624
|
+
const stored = sessionStorage.getItem(key(siteId, submissionId));
|
|
1625
|
+
const parsed = schema.safeParse(stored ? JSON.parse(stored) : null);
|
|
1626
|
+
if (!parsed.success || Date.now() - parsed.data.timestamp > STALE_THRESHOLD_MS) {
|
|
1627
|
+
clearPendingRegistration(siteId, submissionId);
|
|
1487
1628
|
return null;
|
|
1488
1629
|
}
|
|
1489
|
-
return
|
|
1490
|
-
...entry,
|
|
1491
|
-
ticketSelections
|
|
1492
|
-
};
|
|
1630
|
+
return parsed.data;
|
|
1493
1631
|
} catch {
|
|
1494
|
-
console.warn("[sessionStorage] Failed to parse entry, removing");
|
|
1495
|
-
sessionStorage.removeItem(key);
|
|
1496
1632
|
return null;
|
|
1497
1633
|
}
|
|
1498
1634
|
}
|
|
1499
|
-
function
|
|
1500
|
-
if (entry.version === 5) return entry;
|
|
1501
|
-
const { attendeeId, ...stored } = entry;
|
|
1502
|
-
return {
|
|
1503
|
-
...stored,
|
|
1504
|
-
version: 5,
|
|
1505
|
-
reference: {
|
|
1506
|
-
scope: "occurrence",
|
|
1507
|
-
attendeeId
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
/**
|
|
1512
|
-
* Clear pending registration data after successful verification.
|
|
1513
|
-
*
|
|
1514
|
-
* @param siteId - Site ID
|
|
1515
|
-
* @param checkoutAttemptId - Checkout return correlation ID
|
|
1516
|
-
*/
|
|
1517
|
-
function clearPendingRegistration(siteId, checkoutAttemptId) {
|
|
1518
|
-
if (!isStorageAvailable()) return;
|
|
1635
|
+
function clearPendingRegistration(siteId, submissionId) {
|
|
1519
1636
|
try {
|
|
1520
|
-
|
|
1521
|
-
sessionStorage.removeItem(key);
|
|
1637
|
+
sessionStorage.removeItem(key(siteId, submissionId));
|
|
1522
1638
|
} catch {}
|
|
1523
1639
|
}
|
|
1524
|
-
/**
|
|
1525
|
-
* Clean up all stale registration entries from sessionStorage.
|
|
1526
|
-
* Should be called on component mount to prevent accumulation.
|
|
1527
|
-
*/
|
|
1528
1640
|
function cleanupStaleRegistrations() {
|
|
1529
|
-
if (!isStorageAvailable()) return;
|
|
1530
1641
|
try {
|
|
1531
|
-
|
|
1532
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
1533
|
-
const key = sessionStorage.key(i);
|
|
1534
|
-
if (!key?.startsWith(STORAGE_PREFIX)) continue;
|
|
1642
|
+
Array.from({ length: sessionStorage.length }, (_, index) => sessionStorage.key(index)).filter((item) => item !== null && item.startsWith(STORAGE_PREFIX)).forEach((item) => {
|
|
1535
1643
|
try {
|
|
1536
|
-
const
|
|
1537
|
-
if (!
|
|
1538
|
-
const entry = JSON.parse(stored);
|
|
1539
|
-
const timestamp = typeof entry.timestamp === "number" ? entry.timestamp : NaN;
|
|
1540
|
-
const age = Date.now() - timestamp;
|
|
1541
|
-
if (!Number.isFinite(timestamp) || age > STALE_THRESHOLD_MS) keysToRemove.push(key);
|
|
1644
|
+
const parsed = schema.safeParse(JSON.parse(sessionStorage.getItem(item) ?? "null"));
|
|
1645
|
+
if (!parsed.success || Date.now() - parsed.data.timestamp > STALE_THRESHOLD_MS) sessionStorage.removeItem(item);
|
|
1542
1646
|
} catch {
|
|
1543
|
-
|
|
1647
|
+
sessionStorage.removeItem(item);
|
|
1544
1648
|
}
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
if (keysToRemove.length > 0) console.info(`[sessionStorage] Cleaned up ${keysToRemove.length} stale entries`);
|
|
1548
|
-
} catch (error) {
|
|
1549
|
-
console.warn("[sessionStorage] Cleanup failed:", error);
|
|
1550
|
-
}
|
|
1649
|
+
});
|
|
1650
|
+
} catch {}
|
|
1551
1651
|
}
|
|
1552
1652
|
//#endregion
|
|
1653
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/constants.ts
|
|
1654
|
+
const EVENT_REGISTRATION_ANCHOR_ID = "event-registration";
|
|
1655
|
+
const EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM = "registration_attempt";
|
|
1656
|
+
//#endregion
|
|
1553
1657
|
//#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.ts
|
|
1554
|
-
const eventRegistrationStripeReturnDeps = {
|
|
1555
|
-
usePaymentVerification,
|
|
1556
|
-
readReturnParams: () => {
|
|
1557
|
-
const params = new URLSearchParams(window.location.search);
|
|
1558
|
-
return {
|
|
1559
|
-
registration: params.get("registration"),
|
|
1560
|
-
registration_attempt: params.get(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM),
|
|
1561
|
-
session_id: params.get("session_id")
|
|
1562
|
-
};
|
|
1563
|
-
},
|
|
1564
|
-
clearReturnParams: () => {
|
|
1565
|
-
consumeSearchParams([
|
|
1566
|
-
"registration",
|
|
1567
|
-
EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM,
|
|
1568
|
-
"session_id"
|
|
1569
|
-
]);
|
|
1570
|
-
},
|
|
1571
|
-
scrollToBlock,
|
|
1572
|
-
cleanupStaleRegistrations,
|
|
1573
|
-
clearPendingRegistration,
|
|
1574
|
-
getPendingRegistration
|
|
1575
|
-
};
|
|
1576
|
-
function restorePendingPresentation(options, pending) {
|
|
1577
|
-
options.setFormData(pending.formData);
|
|
1578
|
-
options.setTicketSelections(pending.ticketSelections);
|
|
1579
|
-
options.setTicketsCount(getTicketSelectionTotal(pending.ticketSelections));
|
|
1580
|
-
if (pending.selectedEventId) {
|
|
1581
|
-
const event = options.events.find((item) => item.selectionId === pending.selectedEventId) ?? null;
|
|
1582
|
-
if (event) options.setSelectedEvent(event);
|
|
1583
|
-
}
|
|
1584
|
-
options.setSelectedAttendanceMode(pending.selectedAttendanceMode ?? null);
|
|
1585
|
-
options.setPaymentSelection(pending.paymentSelection);
|
|
1586
|
-
}
|
|
1587
|
-
function showReturnReconciliation(options, message) {
|
|
1588
|
-
options.setErrorMessage(message);
|
|
1589
|
-
options.setStep("verifying-timeout");
|
|
1590
|
-
}
|
|
1591
|
-
function checkoutReturnCommand(params) {
|
|
1592
|
-
const checkoutAttemptId = params.registration_attempt;
|
|
1593
|
-
if (!checkoutAttemptId) return null;
|
|
1594
|
-
if (params.registration === "cancelled") return {
|
|
1595
|
-
kind: "abandon",
|
|
1596
|
-
checkoutAttemptId
|
|
1597
|
-
};
|
|
1598
|
-
if (params.registration === "success" && params.session_id) return {
|
|
1599
|
-
kind: "resolveSuccess",
|
|
1600
|
-
checkoutAttemptId,
|
|
1601
|
-
sessionId: params.session_id
|
|
1602
|
-
};
|
|
1603
|
-
return null;
|
|
1604
|
-
}
|
|
1605
|
-
function handleCheckoutReturnOutcome(options, deps, context, outcome) {
|
|
1606
|
-
const { pending, params } = context;
|
|
1607
|
-
if (outcome.kind === "resolved") {
|
|
1608
|
-
deps.clearReturnParams();
|
|
1609
|
-
options.setPendingRegistrationReference(outcome.reference);
|
|
1610
|
-
options.setStep("verifying-payment");
|
|
1611
|
-
return "settled";
|
|
1612
|
-
}
|
|
1613
|
-
if (outcome.kind === "abandoned") {
|
|
1614
|
-
deps.clearReturnParams();
|
|
1615
|
-
options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
|
|
1616
|
-
options.setStep("select-payment");
|
|
1617
|
-
return "settled";
|
|
1618
|
-
}
|
|
1619
|
-
if (outcome.kind === "notFound" && params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
|
|
1620
|
-
deps.clearReturnParams();
|
|
1621
|
-
options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
|
|
1622
|
-
options.setStep("select-payment");
|
|
1623
|
-
return "settled";
|
|
1624
|
-
}
|
|
1625
|
-
showReturnReconciliation(options, "We’re checking your booking and payment. Please don’t try to book again yet. If you’ve already paid, you don’t need to pay again.");
|
|
1626
|
-
return outcome.kind === "notFound" ? "settled" : "retryable";
|
|
1627
|
-
}
|
|
1628
|
-
async function handleCheckoutReturn(options, deps, returnContextRef) {
|
|
1629
|
-
deps.cleanupStaleRegistrations();
|
|
1630
|
-
const params = deps.readReturnParams();
|
|
1631
|
-
if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
|
|
1632
|
-
deps.scrollToBlock(EVENT_REGISTRATION_ANCHOR_ID);
|
|
1633
|
-
const siteId = options.api.config.siteId;
|
|
1634
|
-
const command = checkoutReturnCommand(params);
|
|
1635
|
-
if (!siteId) return "settled";
|
|
1636
|
-
if (!command) {
|
|
1637
|
-
showReturnReconciliation(options, "We’re checking your booking and payment. Please don’t try to book again yet. If you’ve already paid, you don’t need to pay again.");
|
|
1638
|
-
return "settled";
|
|
1639
|
-
}
|
|
1640
|
-
const { checkoutAttemptId } = command;
|
|
1641
|
-
const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
|
|
1642
|
-
returnContextRef.current = {
|
|
1643
|
-
checkoutAttemptId,
|
|
1644
|
-
mailingListSignupNote: pending?.mailingListSignupNote
|
|
1645
|
-
};
|
|
1646
|
-
if (pending) restorePendingPresentation(options, pending);
|
|
1647
|
-
options.setStep("verifying-payment");
|
|
1648
|
-
try {
|
|
1649
|
-
const outcome = await options.api.resolveEventCheckoutReturn(command);
|
|
1650
|
-
return handleCheckoutReturnOutcome(options, deps, {
|
|
1651
|
-
pending,
|
|
1652
|
-
params
|
|
1653
|
-
}, outcome);
|
|
1654
|
-
} catch {
|
|
1655
|
-
showReturnReconciliation(options, "We couldn’t check your payment just now. Please check again in a moment. Don’t make another booking while we’re checking.");
|
|
1656
|
-
}
|
|
1657
|
-
return "retryable";
|
|
1658
|
-
}
|
|
1659
1658
|
function useEventRegistrationStripeReturn(options) {
|
|
1660
|
-
|
|
1661
|
-
}
|
|
1662
|
-
function completeVerifiedRegistration(options, deps, returnContext) {
|
|
1663
|
-
const siteId = options.api.config.siteId;
|
|
1664
|
-
const checkoutAttemptId = returnContext?.checkoutAttemptId;
|
|
1665
|
-
if (siteId && checkoutAttemptId) deps.clearPendingRegistration(siteId, checkoutAttemptId);
|
|
1666
|
-
options.setRegistrationResult({
|
|
1667
|
-
status: "confirmed",
|
|
1668
|
-
mailingListSignupNote: returnContext?.mailingListSignupNote
|
|
1669
|
-
});
|
|
1670
|
-
options.setStep("success");
|
|
1671
|
-
}
|
|
1672
|
-
function useEventRegistrationStripeReturnWithDeps(options, deps) {
|
|
1673
|
-
const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
|
|
1674
|
-
const returnContextRef = useRef(null);
|
|
1675
|
-
const lookupInFlightRef = useRef(false);
|
|
1676
|
-
const [lookupState, setLookupState] = useState("settled");
|
|
1677
|
-
const resolveReturn = useCallback(async () => {
|
|
1678
|
-
if (lookupInFlightRef.current) return;
|
|
1679
|
-
lookupInFlightRef.current = true;
|
|
1680
|
-
setLookupState("loading");
|
|
1681
|
-
try {
|
|
1682
|
-
setLookupState(await handleCheckoutReturn(options, deps, returnContextRef));
|
|
1683
|
-
} finally {
|
|
1684
|
-
lookupInFlightRef.current = false;
|
|
1685
|
-
}
|
|
1686
|
-
}, [options, deps]);
|
|
1687
|
-
const verificationConfig = useMemo(() => step === "verifying-payment" && pendingRegistrationReference ? { reference: pendingRegistrationReference } : null, [step, pendingRegistrationReference]);
|
|
1688
|
-
const verification = deps.usePaymentVerification(verificationConfig);
|
|
1689
|
-
const hasVerificationTimeout = step === "verifying-timeout" && pendingRegistrationReference !== null;
|
|
1690
|
-
const retry = useCallback(() => {
|
|
1691
|
-
if (hasVerificationTimeout) {
|
|
1692
|
-
setStep("verifying-payment");
|
|
1693
|
-
return;
|
|
1694
|
-
}
|
|
1695
|
-
verification.retry();
|
|
1696
|
-
}, [
|
|
1697
|
-
hasVerificationTimeout,
|
|
1698
|
-
setStep,
|
|
1699
|
-
verification.retry
|
|
1700
|
-
]);
|
|
1701
|
-
useEffect(() => {
|
|
1702
|
-
resolveReturn();
|
|
1703
|
-
}, []);
|
|
1659
|
+
const restored = useRef(false);
|
|
1704
1660
|
useEffect(() => {
|
|
1705
|
-
if (
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
]);
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1661
|
+
if (restored.current) return;
|
|
1662
|
+
restored.current = true;
|
|
1663
|
+
cleanupStaleRegistrations();
|
|
1664
|
+
const params = new URLSearchParams(window.location.search);
|
|
1665
|
+
const checkoutAttemptId = params.get(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM);
|
|
1666
|
+
const sessionId = params.get("session_id");
|
|
1667
|
+
if (params.get("registration") !== "payment" || !checkoutAttemptId || !sessionId) return;
|
|
1668
|
+
const pending = options.siteId ? getPendingRegistration(options.siteId, checkoutAttemptId) : null;
|
|
1669
|
+
if (pending) options.restore(pending);
|
|
1670
|
+
options.verify({
|
|
1671
|
+
kind: "resolveSession",
|
|
1672
|
+
checkoutAttemptId,
|
|
1673
|
+
sessionId
|
|
1674
|
+
});
|
|
1675
|
+
}, [options]);
|
|
1676
|
+
}
|
|
1677
|
+
//#endregion
|
|
1678
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/paymentLocation.ts
|
|
1679
|
+
function eventPaymentReturnUrl(submissionId) {
|
|
1680
|
+
const url = new URL(window.location.href);
|
|
1681
|
+
url.searchParams.set("registration", "payment");
|
|
1682
|
+
url.searchParams.set(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM, submissionId);
|
|
1683
|
+
url.searchParams.delete("session_id");
|
|
1684
|
+
url.hash = EVENT_REGISTRATION_ANCHOR_ID;
|
|
1685
|
+
return url.toString();
|
|
1686
|
+
}
|
|
1687
|
+
function rememberEventPayment(submissionId, sessionId) {
|
|
1688
|
+
const url = new URL(eventPaymentReturnUrl(submissionId));
|
|
1689
|
+
url.searchParams.set("session_id", sessionId);
|
|
1690
|
+
window.history.replaceState(window.history.state, "", url);
|
|
1691
|
+
}
|
|
1692
|
+
function clearEventPayment(siteId) {
|
|
1693
|
+
const url = new URL(window.location.href);
|
|
1694
|
+
const submissionId = url.searchParams.get(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM);
|
|
1695
|
+
if (siteId && submissionId) clearPendingRegistration(siteId, submissionId);
|
|
1696
|
+
url.searchParams.delete("registration");
|
|
1697
|
+
url.searchParams.delete(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM);
|
|
1698
|
+
url.searchParams.delete("session_id");
|
|
1699
|
+
window.history.replaceState(window.history.state, "", url);
|
|
1728
1700
|
}
|
|
1729
1701
|
//#endregion
|
|
1730
1702
|
//#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.ts
|
|
@@ -1771,7 +1743,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
|
|
|
1771
1743
|
*/
|
|
1772
1744
|
const { commandRef } = options;
|
|
1773
1745
|
const inFlightRef = useRef(null);
|
|
1774
|
-
const submit = async (method, passId, membershipId, selection
|
|
1746
|
+
const submit = async (method, passId, membershipId, selection) => {
|
|
1775
1747
|
const target = options.registrationTarget;
|
|
1776
1748
|
if (!target) return {
|
|
1777
1749
|
ok: false,
|
|
@@ -1781,25 +1753,21 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
|
|
|
1781
1753
|
options.setStep("submitting");
|
|
1782
1754
|
const registrationCommandId = commandRef.current?.id ?? deps.createClientCommandId();
|
|
1783
1755
|
try {
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
const
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
};
|
|
1792
|
-
request = buildRegistrationRequest(options, prepared, method, passId, membershipId);
|
|
1793
|
-
}
|
|
1756
|
+
const initialPrepared = prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection);
|
|
1757
|
+
const intentKey = registrationRequestIntentKey(buildRegistrationRequest(options, initialPrepared, method, passId, membershipId));
|
|
1758
|
+
const prepared = commandRef.current !== null && commandRef.current.intentKey !== intentKey ? {
|
|
1759
|
+
...initialPrepared,
|
|
1760
|
+
registrationCommandId: deps.createClientCommandId()
|
|
1761
|
+
} : initialPrepared;
|
|
1762
|
+
const request = buildRegistrationRequest(options, prepared, method, passId, membershipId);
|
|
1794
1763
|
commandRef.current = {
|
|
1795
1764
|
id: prepared.registrationCommandId,
|
|
1796
1765
|
intentKey
|
|
1797
1766
|
};
|
|
1798
|
-
|
|
1767
|
+
return await completeRegistrationSubmission(options, deps, prepared, await options.api.registerForEvent({
|
|
1799
1768
|
...request,
|
|
1800
1769
|
registrationCommandId: prepared.registrationCommandId
|
|
1801
|
-
});
|
|
1802
|
-
return await completeRegistrationSubmission(options, deps, prepared, result, submitOptions);
|
|
1770
|
+
}));
|
|
1803
1771
|
} catch (error) {
|
|
1804
1772
|
if (error instanceof BlockApiError && error.tag === "registrationPreparationChanged") commandRef.current = null;
|
|
1805
1773
|
const { message, action } = registrationErrorPresentation(error);
|
|
@@ -1823,7 +1791,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
|
|
|
1823
1791
|
return pending;
|
|
1824
1792
|
};
|
|
1825
1793
|
}
|
|
1826
|
-
async function completeRegistrationSubmission(options, deps, prepared, result
|
|
1794
|
+
async function completeRegistrationSubmission(options, deps, prepared, result) {
|
|
1827
1795
|
if (result.demoBookingPass) storeDemoBookingPass(options.formData.email, result.demoBookingPass);
|
|
1828
1796
|
switch (result.kind) {
|
|
1829
1797
|
case "paymentStatusRequired":
|
|
@@ -1838,10 +1806,24 @@ async function completeRegistrationSubmission(options, deps, prepared, result, s
|
|
|
1838
1806
|
outcome: { kind: "payment_status" }
|
|
1839
1807
|
};
|
|
1840
1808
|
case "checkoutRequired":
|
|
1841
|
-
options.setPendingRegistrationReference(result.reference);
|
|
1842
1809
|
savePendingCheckout(options, deps, prepared, result.submissionId, result.reference, result.mailingListSignupNote);
|
|
1843
|
-
|
|
1844
|
-
|
|
1810
|
+
const subject = result.reference.scope === "occurrence" ? {
|
|
1811
|
+
scope: "occurrence",
|
|
1812
|
+
attendeeId: result.reference.attendeeId,
|
|
1813
|
+
submissionId: result.submissionId
|
|
1814
|
+
} : {
|
|
1815
|
+
scope: "wholeSeries",
|
|
1816
|
+
submissionId: result.submissionId
|
|
1817
|
+
};
|
|
1818
|
+
options.openPayment(result.checkout, subject, result.mailingListSignupNote);
|
|
1819
|
+
return {
|
|
1820
|
+
ok: true,
|
|
1821
|
+
outcome: {
|
|
1822
|
+
kind: "checkout_ready",
|
|
1823
|
+
checkout: result.checkout,
|
|
1824
|
+
subject
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1845
1827
|
case "waitlisted":
|
|
1846
1828
|
options.setRegistrationResult({
|
|
1847
1829
|
status: "waitlist",
|
|
@@ -1853,7 +1835,6 @@ async function completeRegistrationSubmission(options, deps, prepared, result, s
|
|
|
1853
1835
|
break;
|
|
1854
1836
|
case "bookingComplete":
|
|
1855
1837
|
case "paymentDeferred":
|
|
1856
|
-
options.setPendingRegistrationReference(result.reference);
|
|
1857
1838
|
options.setRegistrationResult({
|
|
1858
1839
|
status: "confirmed",
|
|
1859
1840
|
message: result.summary.paymentMethod === "pass" ? result.summary.message : void 0,
|
|
@@ -1926,19 +1907,12 @@ function savePendingCheckout(options, deps, prepared, checkoutAttemptId, referen
|
|
|
1926
1907
|
selectedEventId: options.registrationTarget?.selectedEventId ?? null,
|
|
1927
1908
|
selectedAttendanceMode: options.selectedAttendanceMode,
|
|
1928
1909
|
paymentSelection: prepared.selectionSnapshot,
|
|
1910
|
+
guests: options.guests,
|
|
1911
|
+
selectedSlidingAmount: options.selectedSlidingAmount,
|
|
1912
|
+
discountCode: options.discountCode,
|
|
1929
1913
|
mailingListSignupNote
|
|
1930
1914
|
});
|
|
1931
1915
|
}
|
|
1932
|
-
function redirectToCheckout(checkoutUrl, submitOptions) {
|
|
1933
|
-
if (submitOptions?.redirect !== "manual") window.location.href = checkoutUrl;
|
|
1934
|
-
return {
|
|
1935
|
-
ok: true,
|
|
1936
|
-
outcome: {
|
|
1937
|
-
kind: "checkout_redirect",
|
|
1938
|
-
checkoutUrl
|
|
1939
|
-
}
|
|
1940
|
-
};
|
|
1941
|
-
}
|
|
1942
1916
|
function registrationRequestIntentKey(request) {
|
|
1943
1917
|
const { registrationCommandId: _command, altchaSolution: _proof, mailingListOptIn: _marketingConsent, checkout, ...intent } = request;
|
|
1944
1918
|
return JSON.stringify({
|
|
@@ -1953,7 +1927,7 @@ function buildRegistrationRequest(options, prepared, method, passId, membershipI
|
|
|
1953
1927
|
return {
|
|
1954
1928
|
registrationCommandId: prepared.registrationCommandId,
|
|
1955
1929
|
checkout: {
|
|
1956
|
-
|
|
1930
|
+
returnUrl: eventPaymentReturnUrl(prepared.registrationCommandId),
|
|
1957
1931
|
discountCode: options.discountCode.trim() || void 0,
|
|
1958
1932
|
collectionOptionKind: prepared.selectionSnapshot.method === "stripe" ? prepared.selectionSnapshot.collectionOptionKind : void 0
|
|
1959
1933
|
},
|
|
@@ -2075,7 +2049,7 @@ function getRegistrationOccurrenceId(target) {
|
|
|
2075
2049
|
return target?.subject.kind === "occurrence" ? target.subject.occurrenceId : "";
|
|
2076
2050
|
}
|
|
2077
2051
|
function shouldLoadRegistrationOptions(authState, step) {
|
|
2078
|
-
return authState === "authenticated" || step === "select-tickets" || step === "enter-details" || step === "select-payment";
|
|
2052
|
+
return authState === "authenticated" || step === "select-tickets" || step === "fully-booked" || step === "enter-details" || step === "select-payment";
|
|
2079
2053
|
}
|
|
2080
2054
|
function getEligibilityFlowFlags(input) {
|
|
2081
2055
|
const hasPass = (input.eligibility?.eligiblePasses.length ?? 0) > 0;
|
|
@@ -2174,132 +2148,544 @@ function useWaitlistClaimRestoration(input) {
|
|
|
2174
2148
|
]);
|
|
2175
2149
|
}
|
|
2176
2150
|
//#endregion
|
|
2177
|
-
//#region ../blocks/src/system/runtime/nodes/event-registration/
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2151
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/useGuestRegistrationRecovery.ts
|
|
2152
|
+
/** Guest email is navigation permission only. Discard checks when the visitor
|
|
2153
|
+
* changes identity or selection before the response arrives. */
|
|
2154
|
+
function useGuestRegistrationRecovery(input) {
|
|
2155
|
+
const [checkingNextStep, setCheckingNextStep] = useState(false);
|
|
2156
|
+
const [recoveryMessage, setRecoveryMessage] = useState(null);
|
|
2157
|
+
const request = guestNextStepRequest(input);
|
|
2158
|
+
const requestKey = JSON.stringify([
|
|
2159
|
+
request,
|
|
2160
|
+
input.step,
|
|
2161
|
+
input.portalAuthState,
|
|
2162
|
+
input.admission
|
|
2163
|
+
]);
|
|
2164
|
+
const currentRequest = useRef(requestKey);
|
|
2165
|
+
currentRequest.current = requestKey;
|
|
2166
|
+
const checkDetailsNextStep = () => checkGuestNextStep(input, request, setCheckingNextStep);
|
|
2167
|
+
const handleRecoveryEmail = () => recoverByEmail({
|
|
2168
|
+
...input,
|
|
2169
|
+
checkingNextStep,
|
|
2170
|
+
check: checkDetailsNextStep,
|
|
2171
|
+
setRecoveryMessage,
|
|
2172
|
+
isCurrent: () => currentRequest.current === requestKey
|
|
2173
|
+
});
|
|
2174
|
+
return {
|
|
2175
|
+
checkingNextStep,
|
|
2176
|
+
recoveryMessage,
|
|
2177
|
+
checkDetailsNextStep,
|
|
2178
|
+
handleRecoveryEmail
|
|
2179
|
+
};
|
|
2190
2180
|
}
|
|
2191
|
-
function
|
|
2192
|
-
|
|
2193
|
-
|
|
2181
|
+
async function checkGuestNextStep(input, request, setCheckingNextStep) {
|
|
2182
|
+
const plan = resolveGuestNextStepCheck(input.admission, request);
|
|
2183
|
+
switch (plan.kind) {
|
|
2184
|
+
case "skip": return plan.outcome;
|
|
2185
|
+
case "blocked": return {
|
|
2186
|
+
kind: "unavailable",
|
|
2187
|
+
message: plan.message
|
|
2188
|
+
};
|
|
2189
|
+
case "check": break;
|
|
2190
|
+
}
|
|
2191
|
+
setCheckingNextStep(true);
|
|
2192
|
+
try {
|
|
2193
|
+
return await input.api.checkRegistrationNextStep(plan.request);
|
|
2194
|
+
} catch {
|
|
2195
|
+
return {
|
|
2196
|
+
kind: "unavailable",
|
|
2197
|
+
message: "We could not check availability. Please try again."
|
|
2198
|
+
};
|
|
2199
|
+
} finally {
|
|
2200
|
+
setCheckingNextStep(false);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
async function recoverByEmail(input) {
|
|
2204
|
+
if (input.checkingNextStep) return {
|
|
2205
|
+
ok: false,
|
|
2206
|
+
reason: "blocked",
|
|
2207
|
+
message: "Your booking is being checked."
|
|
2208
|
+
};
|
|
2209
|
+
const emailError = validateFormData(input.formData).email;
|
|
2210
|
+
input.setErrors(emailError ? { email: emailError } : {});
|
|
2211
|
+
input.setRecoveryMessage(null);
|
|
2212
|
+
if (emailError) return {
|
|
2213
|
+
ok: false,
|
|
2214
|
+
reason: "validation-failed",
|
|
2215
|
+
message: emailError
|
|
2216
|
+
};
|
|
2217
|
+
const next = await input.check();
|
|
2218
|
+
if (!input.isCurrent()) return {
|
|
2219
|
+
ok: false,
|
|
2220
|
+
reason: "blocked",
|
|
2221
|
+
message: "Your booking details changed. Please check again."
|
|
2222
|
+
};
|
|
2223
|
+
switch (next.kind) {
|
|
2224
|
+
case "continue":
|
|
2225
|
+
case "waitlist":
|
|
2226
|
+
input.onRecoveryChecked(next);
|
|
2227
|
+
return { ok: true };
|
|
2228
|
+
case "unavailable":
|
|
2229
|
+
input.setRecoveryMessage(next.message);
|
|
2230
|
+
return {
|
|
2231
|
+
ok: false,
|
|
2232
|
+
reason: "blocked",
|
|
2233
|
+
message: next.message
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
function guestNextStepRequest(input) {
|
|
2238
|
+
return input.registrationTarget ? {
|
|
2239
|
+
subject: toRegistrationRequestSubject(input.registrationTarget.subject, input.selectedAttendanceMode),
|
|
2240
|
+
selections: input.ticketSelections,
|
|
2241
|
+
selectedSlidingAmount: input.selectedSlidingAmount ?? void 0,
|
|
2242
|
+
email: input.formData.email,
|
|
2243
|
+
altchaSolution: input.altchaSolution
|
|
2244
|
+
} : null;
|
|
2194
2245
|
}
|
|
2195
2246
|
//#endregion
|
|
2196
|
-
//#region ../blocks/src/system/runtime/nodes/event-registration/
|
|
2197
|
-
|
|
2198
|
-
|
|
2247
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/paymentResponse.ts
|
|
2248
|
+
/**
|
|
2249
|
+
* Verified response identity owns every follow-up action, including storage-free returns.
|
|
2250
|
+
* `requested` is the subject the client sent, if any: a subject-bound `notFound`
|
|
2251
|
+
* means the server lost that booking, which no amount of re-checking will fix.
|
|
2252
|
+
*/
|
|
2253
|
+
function planPaymentResponse(response, intent, requested) {
|
|
2254
|
+
switch (response.kind) {
|
|
2255
|
+
case "checkout": return planCheckoutOutcome(response, intent);
|
|
2256
|
+
case "notFound": return requested ? assistance(requested) : { kind: "unresolved" };
|
|
2257
|
+
case "reconciliationRequired": return { kind: "unresolved" };
|
|
2258
|
+
default: return assertNever(response);
|
|
2259
|
+
}
|
|
2199
2260
|
}
|
|
2200
|
-
function
|
|
2261
|
+
function assistance(subject) {
|
|
2201
2262
|
return {
|
|
2202
|
-
kind: "
|
|
2203
|
-
|
|
2204
|
-
|
|
2263
|
+
kind: "recovery",
|
|
2264
|
+
state: {
|
|
2265
|
+
kind: "assistanceRequired",
|
|
2266
|
+
subject
|
|
2267
|
+
}
|
|
2205
2268
|
};
|
|
2206
2269
|
}
|
|
2207
|
-
function
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2270
|
+
function planCheckoutOutcome({ outcome, subject }, intent) {
|
|
2271
|
+
switch (outcome.kind) {
|
|
2272
|
+
case "resolved": return { kind: "confirmed" };
|
|
2273
|
+
case "paymentAvailable": return intent === "resume" ? {
|
|
2274
|
+
kind: "payment",
|
|
2275
|
+
checkout: outcome.checkout,
|
|
2276
|
+
subject
|
|
2277
|
+
} : {
|
|
2278
|
+
kind: "recovery",
|
|
2279
|
+
state: {
|
|
2280
|
+
kind: "paymentAvailable",
|
|
2281
|
+
checkout: outcome.checkout,
|
|
2282
|
+
subject
|
|
2283
|
+
}
|
|
2284
|
+
};
|
|
2285
|
+
case "abandoned":
|
|
2286
|
+
case "paymentRetryAvailable": return intent === "abandon" ? { kind: "restartBooking" } : {
|
|
2287
|
+
kind: "recovery",
|
|
2288
|
+
state: {
|
|
2289
|
+
kind: "retryAvailable",
|
|
2290
|
+
subject
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
case "paymentProcessing": return {
|
|
2294
|
+
kind: "recovery",
|
|
2295
|
+
state: {
|
|
2296
|
+
kind: "processing",
|
|
2297
|
+
subject
|
|
2298
|
+
}
|
|
2299
|
+
};
|
|
2300
|
+
case "paymentCheckUnavailable": return {
|
|
2301
|
+
kind: "recovery",
|
|
2302
|
+
state: {
|
|
2303
|
+
kind: "temporarilyUnavailable",
|
|
2304
|
+
subject
|
|
2305
|
+
}
|
|
2306
|
+
};
|
|
2307
|
+
case "reconciliationRequired":
|
|
2308
|
+
case "paymentAssistanceRequired": return assistance(subject);
|
|
2309
|
+
default: return assertNever(outcome);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
function paymentCheckEvidence(result) {
|
|
2313
|
+
switch (result.kind) {
|
|
2314
|
+
case "unreachable": return { kind: "inconclusive" };
|
|
2315
|
+
case "notFound": return { kind: "bindingLost" };
|
|
2316
|
+
case "reconciliationRequired": return { kind: "paymentUnderway" };
|
|
2317
|
+
case "checkout": return checkoutEvidence(result);
|
|
2318
|
+
default: return assertNever(result);
|
|
2217
2319
|
}
|
|
2320
|
+
}
|
|
2321
|
+
function checkoutEvidence(response) {
|
|
2322
|
+
const { outcome } = response;
|
|
2218
2323
|
switch (outcome.kind) {
|
|
2324
|
+
case "paymentCheckUnavailable": return { kind: "inconclusive" };
|
|
2325
|
+
case "paymentAvailable": return {
|
|
2326
|
+
kind: "payable",
|
|
2327
|
+
checkout: outcome.checkout,
|
|
2328
|
+
response
|
|
2329
|
+
};
|
|
2219
2330
|
case "paymentProcessing":
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
});
|
|
2224
|
-
return;
|
|
2225
|
-
case "paymentAvailable":
|
|
2226
|
-
setRecovery({
|
|
2227
|
-
...recovery,
|
|
2228
|
-
kind: "paymentAvailable",
|
|
2229
|
-
checkoutUrl: outcome.checkoutUrl
|
|
2230
|
-
});
|
|
2231
|
-
return;
|
|
2232
|
-
case "paymentCheckUnavailable":
|
|
2233
|
-
setRecovery({
|
|
2234
|
-
...recovery,
|
|
2235
|
-
kind: "temporarilyUnavailable"
|
|
2236
|
-
});
|
|
2237
|
-
return;
|
|
2331
|
+
case "reconciliationRequired": return { kind: "paymentUnderway" };
|
|
2332
|
+
case "resolved":
|
|
2333
|
+
case "abandoned":
|
|
2238
2334
|
case "paymentRetryAvailable":
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2335
|
+
case "paymentAssistanceRequired": return {
|
|
2336
|
+
kind: "terminal",
|
|
2337
|
+
response
|
|
2338
|
+
};
|
|
2339
|
+
default: return assertNever(outcome);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
function planMountedPaymentRefresh(result, mounted) {
|
|
2343
|
+
const evidence = paymentCheckEvidence(result);
|
|
2344
|
+
switch (evidence.kind) {
|
|
2345
|
+
case "inconclusive": return { kind: "stay" };
|
|
2346
|
+
case "paymentUnderway": return { kind: "poll" };
|
|
2347
|
+
case "bindingLost": return {
|
|
2348
|
+
kind: "leave",
|
|
2349
|
+
transition: assistance(mounted.subject)
|
|
2350
|
+
};
|
|
2351
|
+
case "payable": return evidence.checkout.sessionId === mounted.sessionId ? { kind: "stay" } : {
|
|
2352
|
+
kind: "leave",
|
|
2353
|
+
transition: planCheckoutOutcome(evidence.response, "resume")
|
|
2354
|
+
};
|
|
2355
|
+
case "terminal": return {
|
|
2356
|
+
kind: "leave",
|
|
2357
|
+
transition: planCheckoutOutcome(evidence.response, "check")
|
|
2358
|
+
};
|
|
2359
|
+
default: return assertNever(evidence);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
function paymentResponseNeedsPolling(response) {
|
|
2363
|
+
if (response.kind !== "checkout") return response.kind === "reconciliationRequired";
|
|
2364
|
+
return paymentOutcomeNeedsPolling(response.outcome);
|
|
2365
|
+
}
|
|
2366
|
+
function paymentOutcomeNeedsPolling(outcome) {
|
|
2367
|
+
return outcome.kind === "paymentProcessing" || outcome.kind === "reconciliationRequired" || outcome.kind === "paymentCheckUnavailable";
|
|
2368
|
+
}
|
|
2369
|
+
//#endregion
|
|
2370
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.ts
|
|
2371
|
+
function usePaymentRuntime() {
|
|
2372
|
+
const [state, setState] = useState({ kind: "idle" });
|
|
2373
|
+
const note = useRef(void 0);
|
|
2374
|
+
const operation = useRef(null);
|
|
2375
|
+
const generation = useRef(0);
|
|
2376
|
+
const appliedResult = useRef(null);
|
|
2377
|
+
const current = useRef(state);
|
|
2378
|
+
current.current = state;
|
|
2379
|
+
useEffect(() => () => {
|
|
2380
|
+
generation.current += 1;
|
|
2381
|
+
}, []);
|
|
2382
|
+
return {
|
|
2383
|
+
state,
|
|
2384
|
+
setState,
|
|
2385
|
+
note,
|
|
2386
|
+
operation,
|
|
2387
|
+
generation,
|
|
2388
|
+
appliedResult,
|
|
2389
|
+
current
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
function usePaymentTransitions(options, runtime) {
|
|
2393
|
+
const { setState, note, generation, operation } = runtime;
|
|
2394
|
+
const openPayment = useCallback((checkout, subject, mailingListSignupNote) => {
|
|
2395
|
+
if (mailingListSignupNote) note.current = mailingListSignupNote;
|
|
2396
|
+
rememberEventPayment(subject.submissionId, checkout.sessionId);
|
|
2397
|
+
setState({
|
|
2398
|
+
kind: "payment",
|
|
2399
|
+
checkout,
|
|
2400
|
+
subject
|
|
2401
|
+
});
|
|
2402
|
+
options.setStep("payment");
|
|
2403
|
+
}, [
|
|
2404
|
+
options.setStep,
|
|
2405
|
+
setState,
|
|
2406
|
+
note
|
|
2407
|
+
]);
|
|
2408
|
+
const verify = useCallback((request, resumePayment = true) => {
|
|
2409
|
+
if (operation.current) return;
|
|
2410
|
+
generation.current += 1;
|
|
2411
|
+
setState({
|
|
2412
|
+
kind: "verifying",
|
|
2413
|
+
request,
|
|
2414
|
+
resumePayment
|
|
2415
|
+
});
|
|
2416
|
+
options.setStep("verifying-payment");
|
|
2417
|
+
}, [
|
|
2418
|
+
options.setStep,
|
|
2419
|
+
operation,
|
|
2420
|
+
generation,
|
|
2421
|
+
setState
|
|
2422
|
+
]);
|
|
2423
|
+
const restartFrom = useCallback((step) => {
|
|
2424
|
+
clearEventPayment(options.api.config.siteId);
|
|
2425
|
+
options.beginNewSubmission();
|
|
2426
|
+
setState({ kind: "idle" });
|
|
2427
|
+
options.setOptionsRefreshKey((value) => value + 1);
|
|
2428
|
+
options.setStep(step);
|
|
2429
|
+
}, [options, setState]);
|
|
2430
|
+
const resetBooking = useCallback(() => restartFrom("select-tickets"), [restartFrom]);
|
|
2431
|
+
const returnToPaymentOptions = useCallback(() => restartFrom(options.detailsInPage() ? "select-payment" : "select-tickets"), [restartFrom, options]);
|
|
2432
|
+
const applyTransition = useCallback((transition) => {
|
|
2433
|
+
switch (transition.kind) {
|
|
2434
|
+
case "confirmed":
|
|
2435
|
+
options.setRegistrationResult({
|
|
2436
|
+
status: "confirmed",
|
|
2437
|
+
mailingListSignupNote: note.current
|
|
2438
|
+
});
|
|
2439
|
+
clearEventPayment(options.api.config.siteId);
|
|
2440
|
+
setState({ kind: "idle" });
|
|
2441
|
+
options.setStep("success");
|
|
2442
|
+
return;
|
|
2443
|
+
case "payment": return openPayment(transition.checkout, transition.subject);
|
|
2444
|
+
case "restartBooking": return resetBooking();
|
|
2445
|
+
case "recovery":
|
|
2446
|
+
setState({
|
|
2447
|
+
kind: "recovery",
|
|
2448
|
+
state: {
|
|
2449
|
+
...transition.state,
|
|
2450
|
+
mailingListSignupNote: note.current
|
|
2451
|
+
}
|
|
2452
|
+
});
|
|
2453
|
+
options.setStep("payment-status");
|
|
2454
|
+
return;
|
|
2455
|
+
case "unresolved": return options.setStep("verifying-timeout");
|
|
2456
|
+
default: return assertNever(transition);
|
|
2457
|
+
}
|
|
2458
|
+
}, [
|
|
2459
|
+
openPayment,
|
|
2460
|
+
resetBooking,
|
|
2461
|
+
options,
|
|
2462
|
+
setState,
|
|
2463
|
+
note
|
|
2464
|
+
]);
|
|
2465
|
+
return {
|
|
2466
|
+
openPayment,
|
|
2467
|
+
verify,
|
|
2468
|
+
applyOutcome: useCallback((response, intent, requested) => applyTransition(planPaymentResponse(response, intent, requested)), [applyTransition]),
|
|
2469
|
+
applyTransition,
|
|
2470
|
+
resetBooking,
|
|
2471
|
+
returnToPaymentOptions
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
/** A session-only return has no client subject; the server's envelope must supply one. */
|
|
2475
|
+
function requestedSubject(request) {
|
|
2476
|
+
return request.kind === "resolveSession" ? null : request.subject;
|
|
2477
|
+
}
|
|
2478
|
+
function usePaymentChecks(options, runtime, transitions) {
|
|
2479
|
+
const { state, appliedResult, current } = runtime;
|
|
2480
|
+
const request = state.kind === "verifying" ? state.request : null;
|
|
2481
|
+
const polling = usePolling(request ? {
|
|
2482
|
+
restartKey: JSON.stringify(request),
|
|
2483
|
+
pollFn: () => options.api.resolveEventCheckoutReturn(request),
|
|
2484
|
+
isComplete: (result) => !paymentResponseNeedsPolling(result),
|
|
2485
|
+
maxAttempts: 10,
|
|
2486
|
+
interval: 1e3
|
|
2487
|
+
} : null);
|
|
2488
|
+
useEffect(() => {
|
|
2489
|
+
if (polling.status === "completed" && polling.result && request && appliedResult.current !== polling.result) {
|
|
2490
|
+
appliedResult.current = polling.result;
|
|
2491
|
+
transitions.applyOutcome(polling.result, state.kind === "verifying" && state.resumePayment ? "resume" : "check", requestedSubject(request));
|
|
2492
|
+
} else if (polling.status === "timeout") options.setStep("verifying-timeout");
|
|
2493
|
+
}, [
|
|
2494
|
+
polling.status,
|
|
2495
|
+
polling.result,
|
|
2496
|
+
request,
|
|
2497
|
+
transitions.applyOutcome,
|
|
2498
|
+
options.setStep,
|
|
2499
|
+
appliedResult
|
|
2500
|
+
]);
|
|
2501
|
+
const check = useCallback((resumePayment = false) => {
|
|
2502
|
+
appliedResult.current = null;
|
|
2503
|
+
const active = current.current;
|
|
2504
|
+
if (active.kind === "payment") transitions.verify({
|
|
2505
|
+
kind: "checkStatus",
|
|
2506
|
+
subject: active.subject
|
|
2507
|
+
}, resumePayment);
|
|
2508
|
+
else if (active.kind === "recovery") transitions.verify({
|
|
2509
|
+
kind: "checkStatus",
|
|
2510
|
+
subject: active.state.subject
|
|
2511
|
+
}, resumePayment);
|
|
2512
|
+
else if (active.kind === "verifying") {
|
|
2513
|
+
options.setStep("verifying-payment");
|
|
2514
|
+
polling.retry();
|
|
2515
|
+
}
|
|
2516
|
+
}, [
|
|
2517
|
+
transitions.verify,
|
|
2518
|
+
polling.retry,
|
|
2519
|
+
options.setStep,
|
|
2520
|
+
appliedResult,
|
|
2521
|
+
current
|
|
2522
|
+
]);
|
|
2523
|
+
const checkStatus = useCallback(() => check(), [check]);
|
|
2524
|
+
return {
|
|
2525
|
+
check: checkStatus,
|
|
2526
|
+
retryPaymentLoad: useCallback(() => check(true), [check]),
|
|
2527
|
+
verification: {
|
|
2528
|
+
...polling,
|
|
2529
|
+
retry: checkStatus,
|
|
2530
|
+
retryReturn: checkStatus
|
|
2531
|
+
}
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
function usePaymentAbandonment(options, runtime, transitions) {
|
|
2535
|
+
const { operation, generation, current, setState } = runtime;
|
|
2536
|
+
return useCallback(() => {
|
|
2537
|
+
if (operation.current) return operation.current;
|
|
2538
|
+
const active = current.current;
|
|
2539
|
+
if (active.kind !== "payment") return Promise.resolve();
|
|
2540
|
+
const token = ++generation.current;
|
|
2541
|
+
setState({
|
|
2542
|
+
kind: "abandoning",
|
|
2543
|
+
subject: active.subject
|
|
2544
|
+
});
|
|
2545
|
+
const pending = options.api.resolveEventCheckoutReturn({
|
|
2546
|
+
kind: "abandon",
|
|
2547
|
+
subject: active.subject
|
|
2548
|
+
}).then((outcome) => {
|
|
2549
|
+
if (token !== generation.current) return;
|
|
2550
|
+
transitions.applyOutcome(outcome, "abandon", active.subject);
|
|
2551
|
+
}).catch(() => {
|
|
2552
|
+
if (token !== generation.current) return;
|
|
2553
|
+
setState({
|
|
2554
|
+
kind: "recovery",
|
|
2555
|
+
state: {
|
|
2556
|
+
kind: "temporarilyUnavailable",
|
|
2557
|
+
subject: active.subject,
|
|
2558
|
+
mailingListSignupNote: runtime.note.current
|
|
2559
|
+
}
|
|
2560
|
+
});
|
|
2561
|
+
options.setStep("payment-status");
|
|
2562
|
+
});
|
|
2563
|
+
operation.current = pending;
|
|
2564
|
+
pending.finally(() => {
|
|
2565
|
+
if (operation.current === pending) operation.current = null;
|
|
2566
|
+
});
|
|
2567
|
+
return pending;
|
|
2568
|
+
}, [
|
|
2569
|
+
options.api,
|
|
2570
|
+
transitions.applyOutcome,
|
|
2571
|
+
operation,
|
|
2572
|
+
generation,
|
|
2573
|
+
current,
|
|
2574
|
+
setState,
|
|
2575
|
+
options.setStep,
|
|
2576
|
+
runtime.note
|
|
2577
|
+
]);
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Returning to the tab (often from a banking app mid-authentication) must not
|
|
2581
|
+
* unmount Stripe on inconclusive evidence; `planMountedPaymentRefresh` decides.
|
|
2582
|
+
*/
|
|
2583
|
+
function usePaymentBackgroundCheck(options, runtime, transitions) {
|
|
2584
|
+
const { current, generation, operation } = runtime;
|
|
2585
|
+
const inFlight = useRef(false);
|
|
2586
|
+
return useCallback(() => {
|
|
2587
|
+
const active = current.current;
|
|
2588
|
+
if (active.kind !== "payment" || operation.current || inFlight.current) return;
|
|
2589
|
+
const token = generation.current;
|
|
2590
|
+
inFlight.current = true;
|
|
2591
|
+
const request = {
|
|
2592
|
+
kind: "checkStatus",
|
|
2593
|
+
subject: active.subject
|
|
2594
|
+
};
|
|
2595
|
+
const unreachable = { kind: "unreachable" };
|
|
2596
|
+
options.api.resolveEventCheckoutReturn(request).catch(() => unreachable).then((result) => {
|
|
2597
|
+
if (token !== generation.current || current.current !== active) return;
|
|
2598
|
+
const refresh = planMountedPaymentRefresh(result, {
|
|
2599
|
+
sessionId: active.checkout.sessionId,
|
|
2600
|
+
subject: active.subject
|
|
2242
2601
|
});
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2602
|
+
switch (refresh.kind) {
|
|
2603
|
+
case "stay": return;
|
|
2604
|
+
case "poll": return transitions.verify(request, false);
|
|
2605
|
+
case "leave": return transitions.applyTransition(refresh.transition);
|
|
2606
|
+
default: return assertNever(refresh);
|
|
2607
|
+
}
|
|
2608
|
+
}).finally(() => {
|
|
2609
|
+
inFlight.current = false;
|
|
2610
|
+
});
|
|
2611
|
+
}, [
|
|
2612
|
+
options.api,
|
|
2613
|
+
transitions.verify,
|
|
2614
|
+
transitions.applyTransition,
|
|
2615
|
+
current,
|
|
2616
|
+
generation,
|
|
2617
|
+
operation
|
|
2618
|
+
]);
|
|
2619
|
+
}
|
|
2620
|
+
function usePaymentRefresh(state, check) {
|
|
2621
|
+
useEffect(() => {
|
|
2622
|
+
if (state.kind !== "payment") return;
|
|
2623
|
+
const timeout = window.setTimeout(check, Math.max(0, Date.parse(state.checkout.expiresAt) - Date.now()));
|
|
2624
|
+
const visible = () => {
|
|
2625
|
+
if (document.visibilityState === "visible") check();
|
|
2626
|
+
};
|
|
2627
|
+
const restored = (event) => {
|
|
2628
|
+
if (event.persisted) check();
|
|
2629
|
+
};
|
|
2630
|
+
document.addEventListener("visibilitychange", visible);
|
|
2631
|
+
window.addEventListener("pageshow", restored);
|
|
2632
|
+
return () => {
|
|
2633
|
+
clearTimeout(timeout);
|
|
2634
|
+
document.removeEventListener("visibilitychange", visible);
|
|
2635
|
+
window.removeEventListener("pageshow", restored);
|
|
2636
|
+
};
|
|
2637
|
+
}, [state, check]);
|
|
2251
2638
|
}
|
|
2252
2639
|
function usePaymentStatusRecovery(options) {
|
|
2253
|
-
const
|
|
2254
|
-
const
|
|
2640
|
+
const runtime = usePaymentRuntime();
|
|
2641
|
+
const transitions = usePaymentTransitions(options, runtime);
|
|
2642
|
+
const checks = usePaymentChecks(options, runtime, transitions);
|
|
2643
|
+
const backToBooking = usePaymentAbandonment(options, runtime, transitions);
|
|
2644
|
+
const backgroundCheck = usePaymentBackgroundCheck(options, runtime, transitions);
|
|
2645
|
+
const { state, setState, generation, note } = runtime;
|
|
2646
|
+
usePaymentRefresh(state, backgroundCheck);
|
|
2647
|
+
const recovery = state.kind === "recovery" ? state.state : null;
|
|
2255
2648
|
return {
|
|
2649
|
+
state,
|
|
2256
2650
|
recovery,
|
|
2257
|
-
showRecovery: useCallback((next) =>
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
},
|
|
2282
|
-
|
|
2283
|
-
if (recovery?.kind === "
|
|
2284
|
-
}
|
|
2285
|
-
returnToPayment: useCallback(() => {
|
|
2286
|
-
if (recovery?.kind !== "retryAvailable") return;
|
|
2287
|
-
options.beginNewSubmission();
|
|
2288
|
-
setRecovery(null);
|
|
2289
|
-
options.setOptionsRefreshKey((value) => value + 1);
|
|
2290
|
-
options.setStep("select-payment");
|
|
2291
|
-
}, [options, recovery]),
|
|
2292
|
-
returnToBooking: useCallback(() => {
|
|
2293
|
-
if (recovery?.kind !== "assistanceRequired") return;
|
|
2294
|
-
options.beginNewSubmission();
|
|
2295
|
-
setRecovery(null);
|
|
2296
|
-
options.setOptionsRefreshKey((value) => value + 1);
|
|
2297
|
-
options.setStep("select-tickets");
|
|
2298
|
-
}, [options, recovery])
|
|
2651
|
+
showRecovery: useCallback((next) => {
|
|
2652
|
+
generation.current += 1;
|
|
2653
|
+
note.current = next?.mailingListSignupNote;
|
|
2654
|
+
setState(next ? {
|
|
2655
|
+
kind: "recovery",
|
|
2656
|
+
state: next
|
|
2657
|
+
} : { kind: "idle" });
|
|
2658
|
+
}, [
|
|
2659
|
+
generation,
|
|
2660
|
+
note,
|
|
2661
|
+
setState
|
|
2662
|
+
]),
|
|
2663
|
+
restoreMailingListNote: (value) => {
|
|
2664
|
+
note.current = value;
|
|
2665
|
+
},
|
|
2666
|
+
openPayment: transitions.openPayment,
|
|
2667
|
+
verify: transitions.verify,
|
|
2668
|
+
...checks,
|
|
2669
|
+
backToBooking,
|
|
2670
|
+
continuePayment: () => {
|
|
2671
|
+
if (recovery?.kind === "paymentAvailable") transitions.openPayment(recovery.checkout, recovery.subject);
|
|
2672
|
+
},
|
|
2673
|
+
returnToPayment: () => {
|
|
2674
|
+
if (recovery?.kind === "retryAvailable") transitions.returnToPaymentOptions();
|
|
2675
|
+
},
|
|
2676
|
+
returnToBooking: () => {
|
|
2677
|
+
if (recovery?.kind === "retryAvailable") transitions.resetBooking();
|
|
2678
|
+
}
|
|
2299
2679
|
};
|
|
2300
2680
|
}
|
|
2301
2681
|
//#endregion
|
|
2302
2682
|
//#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
|
|
2683
|
+
function resolveAvailableAttendanceMode(event, selected) {
|
|
2684
|
+
if (!event) return null;
|
|
2685
|
+
const mode = event.deliveryMode ?? "in_person";
|
|
2686
|
+
if (mode === "online" || mode === "in_person") return mode;
|
|
2687
|
+
return selected && event.attendanceModes?.some((item) => item.mode === selected) ? selected : null;
|
|
2688
|
+
}
|
|
2303
2689
|
function usesTieredPricing(event) {
|
|
2304
2690
|
return event !== null && event !== void 0 ? publicEventPricingStrategy(event.pricing) === "tiered" : false;
|
|
2305
2691
|
}
|
|
@@ -2357,19 +2743,24 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2357
2743
|
const [errorAction, setErrorAction] = useState("retry");
|
|
2358
2744
|
const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
|
|
2359
2745
|
const [altchaSolution, setAltchaSolution] = useState(null);
|
|
2360
|
-
const [checkingNextStep, setCheckingNextStep] = useState(false);
|
|
2361
2746
|
const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
|
|
2362
2747
|
const waitlistClaim = deps.useWaitlistClaim();
|
|
2363
2748
|
const [showMagicLinkModal, setShowMagicLinkModal] = useState(false);
|
|
2364
|
-
const [pendingRegistrationReference, setPendingRegistrationReference] = useState(null);
|
|
2365
2749
|
const [paymentStepState, setPaymentStepState] = useState({
|
|
2366
2750
|
status: "loading",
|
|
2367
2751
|
canContinue: false,
|
|
2368
2752
|
buttonText: "Continue",
|
|
2369
2753
|
showLoginPrompt: false
|
|
2370
2754
|
});
|
|
2755
|
+
const selectionLocked = [
|
|
2756
|
+
"payment",
|
|
2757
|
+
"submitting",
|
|
2758
|
+
"verifying-payment",
|
|
2759
|
+
"verifying-timeout",
|
|
2760
|
+
"payment-status"
|
|
2761
|
+
].includes(step);
|
|
2371
2762
|
useEffect(() => {
|
|
2372
|
-
if (portalAuthState !== "authenticated" || !portalProfile) return;
|
|
2763
|
+
if (selectionLocked || portalAuthState !== "authenticated" || !portalProfile) return;
|
|
2373
2764
|
setFormData((prev) => ({
|
|
2374
2765
|
...prev,
|
|
2375
2766
|
email: portalProfile.email,
|
|
@@ -2391,16 +2782,17 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2391
2782
|
step: nextStep
|
|
2392
2783
|
});
|
|
2393
2784
|
}, []);
|
|
2394
|
-
const
|
|
2785
|
+
const paymentController = usePaymentStatusRecovery({
|
|
2395
2786
|
api,
|
|
2396
2787
|
beginNewSubmission: () => {
|
|
2397
2788
|
commandRef.current = null;
|
|
2398
2789
|
},
|
|
2399
|
-
setPendingRegistrationReference,
|
|
2400
2790
|
setRegistrationResult,
|
|
2401
2791
|
setOptionsRefreshKey,
|
|
2402
|
-
setStep
|
|
2792
|
+
setStep,
|
|
2793
|
+
detailsInPage: () => Object.keys(validateFormData(formData)).length === 0
|
|
2403
2794
|
});
|
|
2795
|
+
const { recovery: paymentStatusRecovery, showRecovery: setPaymentStatusRecovery, check: handleCheckPaymentStatus, continuePayment: handleContinueExistingPayment, returnToPayment: handleReturnToPayment, returnToBooking: handleReturnToBooking } = paymentController;
|
|
2404
2796
|
const usesTicketTypeSelections = usesTieredPricing(currentEvent);
|
|
2405
2797
|
const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
|
|
2406
2798
|
event: currentEvent,
|
|
@@ -2502,26 +2894,14 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2502
2894
|
});
|
|
2503
2895
|
}, [portalAuthState]);
|
|
2504
2896
|
useEffect(() => {
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
return;
|
|
2509
|
-
}
|
|
2510
|
-
const deliveryMode = event.deliveryMode ?? "in_person";
|
|
2511
|
-
if (deliveryMode === "online") {
|
|
2512
|
-
if (selectedAttendanceMode !== "online") setSelectedAttendanceMode("online");
|
|
2513
|
-
return;
|
|
2514
|
-
}
|
|
2515
|
-
if (deliveryMode === "in_person") {
|
|
2516
|
-
if (selectedAttendanceMode !== "in_person") setSelectedAttendanceMode("in_person");
|
|
2517
|
-
return;
|
|
2518
|
-
}
|
|
2519
|
-
if (selectedAttendanceMode && (event.attendanceModes ?? []).some((mode) => mode.mode === selectedAttendanceMode)) return;
|
|
2520
|
-
setSelectedAttendanceMode(null);
|
|
2897
|
+
if (selectionLocked) return;
|
|
2898
|
+
const next = resolveAvailableAttendanceMode(selectedEvent ?? preSelectedEvent, selectedAttendanceMode);
|
|
2899
|
+
if (next !== selectedAttendanceMode) setSelectedAttendanceMode(next);
|
|
2521
2900
|
}, [
|
|
2522
2901
|
preSelectedEvent,
|
|
2523
2902
|
selectedAttendanceMode,
|
|
2524
|
-
selectedEvent
|
|
2903
|
+
selectedEvent,
|
|
2904
|
+
selectionLocked
|
|
2525
2905
|
]);
|
|
2526
2906
|
useEffect(() => {
|
|
2527
2907
|
dispatchFlow({
|
|
@@ -2529,25 +2909,26 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2529
2909
|
eligibility
|
|
2530
2910
|
});
|
|
2531
2911
|
}, [eligibility]);
|
|
2532
|
-
const verification =
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2912
|
+
const verification = paymentController.verification;
|
|
2913
|
+
deps.useEventRegistrationStripeReturn({
|
|
2914
|
+
siteId: api.config.siteId,
|
|
2915
|
+
verify: paymentController.verify,
|
|
2916
|
+
restore: (pending) => {
|
|
2917
|
+
paymentController.restoreMailingListNote(pending.mailingListSignupNote);
|
|
2918
|
+
setFormData(pending.formData);
|
|
2919
|
+
setSelectedTicketLines(pending.ticketSelections);
|
|
2920
|
+
setTicketsCount(getTicketSelectionTotal(pending.ticketSelections));
|
|
2921
|
+
setGuests(pending.guests);
|
|
2922
|
+
discountUi.setDiscountCode(pending.discountCode);
|
|
2923
|
+
setSelectedSlidingAmountInput(pending.selectedSlidingAmount === null ? "" : formatCentsAsCurrencyInput(pending.selectedSlidingAmount));
|
|
2924
|
+
setSelectedAttendanceMode(pending.selectedAttendanceMode ?? null);
|
|
2925
|
+
const event = registrationEvents.find((item) => item.selectionId === pending.selectedEventId);
|
|
2926
|
+
if (event) setSelectedEvent(event);
|
|
2927
|
+
dispatchFlow({
|
|
2928
|
+
type: "set-manual-payment-selection",
|
|
2929
|
+
selection: pending.paymentSelection
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2551
2932
|
});
|
|
2552
2933
|
const requireGuestDetails = useMemo(() => {
|
|
2553
2934
|
return (selectedEvent ?? preSelectedEvent)?.requireGuestDetails === true;
|
|
@@ -2591,6 +2972,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2591
2972
|
visibleOptions: visibleTicketTypes
|
|
2592
2973
|
});
|
|
2593
2974
|
useEffect(() => {
|
|
2975
|
+
if (selectionLocked) return;
|
|
2594
2976
|
if (!usesTicketTypeSelections) {
|
|
2595
2977
|
if (selectedTicketLines.length > 0) setSelectedTicketLines([]);
|
|
2596
2978
|
return;
|
|
@@ -2613,6 +2995,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2613
2995
|
usesTicketTypeSelections
|
|
2614
2996
|
]);
|
|
2615
2997
|
useEffect(() => {
|
|
2998
|
+
if (selectionLocked) return;
|
|
2616
2999
|
if (!requireGuestDetails) {
|
|
2617
3000
|
if (guests.length > 0) setGuests([]);
|
|
2618
3001
|
return;
|
|
@@ -2622,19 +3005,16 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2622
3005
|
if (guests.length > 0) setGuests([]);
|
|
2623
3006
|
return;
|
|
2624
3007
|
}
|
|
2625
|
-
if (guests.length !== guestCount) {
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
phone: ""
|
|
2631
|
-
});
|
|
2632
|
-
setGuests(newGuests);
|
|
2633
|
-
}
|
|
3008
|
+
if (guests.length !== guestCount) setGuests(Array.from({ length: guestCount }, (_, index) => guests[index] ?? {
|
|
3009
|
+
name: "",
|
|
3010
|
+
email: "",
|
|
3011
|
+
phone: ""
|
|
3012
|
+
}));
|
|
2634
3013
|
}, [
|
|
2635
3014
|
ticketsCount,
|
|
2636
3015
|
requireGuestDetails,
|
|
2637
|
-
guests
|
|
3016
|
+
guests,
|
|
3017
|
+
selectionLocked
|
|
2638
3018
|
]);
|
|
2639
3019
|
const validateForm = () => {
|
|
2640
3020
|
const newErrors = validateFormData(formData, Boolean(currentEvent?.consent));
|
|
@@ -2663,49 +3043,55 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2663
3043
|
selectedEventSlidingScale,
|
|
2664
3044
|
selectedSlidingAmount
|
|
2665
3045
|
]);
|
|
2666
|
-
const
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
3046
|
+
const { admission, ticketContinuation, handleTicketsComplete, onRecoveryChecked } = useRegistrationAdmission({
|
|
3047
|
+
eligibility,
|
|
3048
|
+
eligibilityError,
|
|
3049
|
+
eligibilityLoading,
|
|
3050
|
+
hasWaitlistClaim: waitlistClaim.claim !== null,
|
|
3051
|
+
isAuthenticated: portalAuthState === "authenticated",
|
|
3052
|
+
existingRegistrationLoading: existingRegistrationResult.isLoading,
|
|
3053
|
+
ticketSelectionRequired: ticketTypeSelectionRequired,
|
|
3054
|
+
hasValidTicketSelection,
|
|
3055
|
+
dispatchFlow
|
|
3056
|
+
});
|
|
3057
|
+
const { checkingNextStep, recoveryMessage, checkDetailsNextStep, handleRecoveryEmail } = useGuestRegistrationRecovery({
|
|
3058
|
+
api,
|
|
3059
|
+
formData,
|
|
3060
|
+
onRecoveryChecked,
|
|
3061
|
+
setErrors,
|
|
3062
|
+
admission,
|
|
3063
|
+
step,
|
|
3064
|
+
portalAuthState,
|
|
3065
|
+
registrationTarget,
|
|
3066
|
+
selectedAttendanceMode,
|
|
3067
|
+
ticketSelections,
|
|
3068
|
+
selectedSlidingAmount,
|
|
3069
|
+
altchaSolution
|
|
3070
|
+
});
|
|
2686
3071
|
const handleDetailsComplete = async () => {
|
|
2687
|
-
const blockDetailsCompletion = (
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
3072
|
+
const blockDetailsCompletion = (blocker) => {
|
|
3073
|
+
if (blocker.kind === "unavailable") {
|
|
3074
|
+
setErrorAction("places");
|
|
3075
|
+
setErrorMessage(blocker.message);
|
|
3076
|
+
setStep("error");
|
|
3077
|
+
}
|
|
2691
3078
|
return {
|
|
2692
3079
|
ok: false,
|
|
2693
3080
|
reason: "blocked",
|
|
2694
|
-
message
|
|
3081
|
+
message: blocker.message
|
|
2695
3082
|
};
|
|
2696
3083
|
};
|
|
2697
|
-
const availabilityBlocker = resolveRegistrationAvailabilityBlocker(
|
|
2698
|
-
hasWaitlistClaim: waitlistClaim.claim !== null,
|
|
2699
|
-
eligibility,
|
|
2700
|
-
eligibilityError
|
|
2701
|
-
});
|
|
3084
|
+
const availabilityBlocker = resolveRegistrationAvailabilityBlocker(admission);
|
|
2702
3085
|
if (availabilityBlocker) return blockDetailsCompletion(availabilityBlocker);
|
|
2703
3086
|
const pricingSelectionBlocker = resolvePricingSelectionBlocker({
|
|
2704
3087
|
selectionRequired: ticketTypeSelectionRequired,
|
|
2705
3088
|
hasValidSelection: hasValidTicketSelection,
|
|
2706
3089
|
slidingScaleSelectionMessage
|
|
2707
3090
|
});
|
|
2708
|
-
if (pricingSelectionBlocker) return blockDetailsCompletion(
|
|
3091
|
+
if (pricingSelectionBlocker) return blockDetailsCompletion({
|
|
3092
|
+
kind: "unavailable",
|
|
3093
|
+
message: pricingSelectionBlocker
|
|
3094
|
+
});
|
|
2709
3095
|
if (!validateForm()) {
|
|
2710
3096
|
setGuestSectionShake(true);
|
|
2711
3097
|
return {
|
|
@@ -2715,7 +3101,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2715
3101
|
};
|
|
2716
3102
|
}
|
|
2717
3103
|
const next = await checkDetailsNextStep();
|
|
2718
|
-
if (next.kind === "unavailable") return blockDetailsCompletion(
|
|
3104
|
+
if (next.kind === "unavailable") return blockDetailsCompletion({
|
|
3105
|
+
kind: "unavailable",
|
|
3106
|
+
message: next.message
|
|
3107
|
+
});
|
|
2719
3108
|
const canJoinWaitlist = next.kind === "waitlist";
|
|
2720
3109
|
if (!canJoinWaitlist && requiresPaymentStep({
|
|
2721
3110
|
eventRequiresPayment,
|
|
@@ -2732,7 +3121,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2732
3121
|
eventRequiresPayment,
|
|
2733
3122
|
canJoinWaitlist
|
|
2734
3123
|
});
|
|
2735
|
-
if (!submitPlan.ok) return blockDetailsCompletion(
|
|
3124
|
+
if (!submitPlan.ok) return blockDetailsCompletion({
|
|
3125
|
+
kind: "unavailable",
|
|
3126
|
+
message: submitPlan.message
|
|
3127
|
+
});
|
|
2736
3128
|
const submitResult = await handleSubmit(submitPlan.args.method, submitPlan.args.passId, submitPlan.args.membershipId, submitPlan.args.selection);
|
|
2737
3129
|
if (!submitResult.ok) return {
|
|
2738
3130
|
ok: false,
|
|
@@ -2780,6 +3172,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2780
3172
|
const selectionMethod = paymentSelection.method;
|
|
2781
3173
|
const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
|
|
2782
3174
|
const handleSubmit = deps.useEventRegistrationSubmit({
|
|
3175
|
+
openPayment: paymentController.openPayment,
|
|
2783
3176
|
commandRef,
|
|
2784
3177
|
setErrorAction,
|
|
2785
3178
|
api,
|
|
@@ -2795,7 +3188,6 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2795
3188
|
discountCode: discountUi.discountCode,
|
|
2796
3189
|
claimToken: waitlistClaim.token,
|
|
2797
3190
|
waitlistEntryId: waitlistClaim.entryId,
|
|
2798
|
-
setPendingRegistrationReference,
|
|
2799
3191
|
setStep,
|
|
2800
3192
|
setRegistrationResult: (result) => setRegistrationResult(result),
|
|
2801
3193
|
setErrorMessage,
|
|
@@ -2817,7 +3209,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2817
3209
|
});
|
|
2818
3210
|
};
|
|
2819
3211
|
const handleRetry = () => recoverRegistrationError(resolveRegistrationRecoveryAction(step, errorAction), {
|
|
2820
|
-
canJoinWaitlist:
|
|
3212
|
+
canJoinWaitlist: admission.kind === "waitlist",
|
|
2821
3213
|
refreshOptions: () => setOptionsRefreshKey((value) => value + 1),
|
|
2822
3214
|
clearError: () => setErrorMessage(""),
|
|
2823
3215
|
setStep,
|
|
@@ -2828,8 +3220,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2828
3220
|
setSelectedTicketLines,
|
|
2829
3221
|
setSelectedSlidingAmountInput,
|
|
2830
3222
|
setSelectedAttendanceMode,
|
|
2831
|
-
setErrorMessage
|
|
2832
|
-
setPendingRegistrationReference
|
|
3223
|
+
setErrorMessage
|
|
2833
3224
|
})
|
|
2834
3225
|
});
|
|
2835
3226
|
const handlePaymentStepStateChange = useCallback((state) => {
|
|
@@ -2860,21 +3251,35 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2860
3251
|
preSelectedEvent,
|
|
2861
3252
|
hasPreSelectedContext,
|
|
2862
3253
|
step,
|
|
2863
|
-
setStep
|
|
3254
|
+
setStep: (next) => {
|
|
3255
|
+
if (!selectionLocked) setStep(next);
|
|
3256
|
+
else if (step === "payment") paymentController.backToBooking();
|
|
3257
|
+
},
|
|
3258
|
+
admission,
|
|
3259
|
+
ticketContinuation,
|
|
3260
|
+
handleTicketsComplete,
|
|
2864
3261
|
selectedEvent,
|
|
2865
|
-
setSelectedEvent
|
|
3262
|
+
setSelectedEvent: (value) => {
|
|
3263
|
+
if (!selectionLocked) setSelectedEvent(value);
|
|
3264
|
+
},
|
|
2866
3265
|
ticketsCount,
|
|
2867
|
-
setTicketsCount
|
|
3266
|
+
setTicketsCount: (value) => {
|
|
3267
|
+
if (!selectionLocked) setTicketsCount(value);
|
|
3268
|
+
},
|
|
2868
3269
|
selectedAttendanceMode,
|
|
2869
|
-
setSelectedAttendanceMode
|
|
3270
|
+
setSelectedAttendanceMode: (value) => {
|
|
3271
|
+
if (!selectionLocked) setSelectedAttendanceMode(value);
|
|
3272
|
+
},
|
|
2870
3273
|
ticketSelections,
|
|
2871
3274
|
selectedTicketLines: normalizedSelectedTicketLines,
|
|
2872
3275
|
setSelectedTicketLines: (lines) => {
|
|
2873
|
-
if (waitlistClaim.claim) return;
|
|
3276
|
+
if (selectionLocked || waitlistClaim.claim) return;
|
|
2874
3277
|
setSelectedTicketLines(normalizeTicketSelectionLines(lines));
|
|
2875
3278
|
},
|
|
2876
3279
|
selectedSlidingAmountInput,
|
|
2877
|
-
setSelectedSlidingAmountInput
|
|
3280
|
+
setSelectedSlidingAmountInput: (value) => {
|
|
3281
|
+
if (!selectionLocked) setSelectedSlidingAmountInput(value);
|
|
3282
|
+
},
|
|
2878
3283
|
selectedSlidingAmount,
|
|
2879
3284
|
selectedEventSlidingScale,
|
|
2880
3285
|
slidingScaleSelectionMessage,
|
|
@@ -2887,10 +3292,14 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2887
3292
|
eventRequiresPayment,
|
|
2888
3293
|
formData,
|
|
2889
3294
|
mailingListSignup,
|
|
2890
|
-
setFormData
|
|
3295
|
+
setFormData: (value) => {
|
|
3296
|
+
if (!selectionLocked) setFormData(value);
|
|
3297
|
+
},
|
|
2891
3298
|
errors,
|
|
2892
3299
|
guests,
|
|
2893
|
-
setGuests
|
|
3300
|
+
setGuests: (value) => {
|
|
3301
|
+
if (!selectionLocked) setGuests(value);
|
|
3302
|
+
},
|
|
2894
3303
|
guestErrors,
|
|
2895
3304
|
guestSectionShake,
|
|
2896
3305
|
handleGuestShakeEnd,
|
|
@@ -2907,7 +3316,9 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2907
3316
|
handleLogout,
|
|
2908
3317
|
paymentStepState,
|
|
2909
3318
|
handlePaymentStepStateChange,
|
|
2910
|
-
handlePaymentSelectionChange
|
|
3319
|
+
handlePaymentSelectionChange: (value) => {
|
|
3320
|
+
if (!selectionLocked) handlePaymentSelectionChange(value);
|
|
3321
|
+
},
|
|
2911
3322
|
paymentSelection,
|
|
2912
3323
|
isMembershipFastTrack,
|
|
2913
3324
|
waitlistClaim,
|
|
@@ -2923,7 +3334,9 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2923
3334
|
hasMembershipEligibility,
|
|
2924
3335
|
requireGuestDetails,
|
|
2925
3336
|
discountCode: discountUi.discountCode,
|
|
2926
|
-
setDiscountCode:
|
|
3337
|
+
setDiscountCode: (value) => {
|
|
3338
|
+
if (!selectionLocked) discountUi.setDiscountCode(value);
|
|
3339
|
+
},
|
|
2927
3340
|
discountUiOpen: discountUi.discountUiOpen,
|
|
2928
3341
|
setDiscountUiOpen: discountUi.setDiscountUiOpen,
|
|
2929
3342
|
discountsAvailable: discountUi.discountsAvailable,
|
|
@@ -2933,49 +3346,31 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2933
3346
|
discountContext,
|
|
2934
3347
|
shouldShowDiscountToggle,
|
|
2935
3348
|
verification,
|
|
3349
|
+
paymentController,
|
|
2936
3350
|
paymentStatusRecovery,
|
|
2937
3351
|
handleCheckPaymentStatus,
|
|
2938
3352
|
handleContinueExistingPayment,
|
|
2939
3353
|
handleReturnToPayment,
|
|
2940
3354
|
handleReturnToBooking,
|
|
3355
|
+
recoveryMessage,
|
|
3356
|
+
checkingNextStep,
|
|
3357
|
+
handleRecoveryEmail,
|
|
2941
3358
|
handleDetailsComplete,
|
|
2942
3359
|
handlePaymentSelect,
|
|
2943
3360
|
handleUseCardInsteadOfPass,
|
|
2944
3361
|
handleBookForSomeoneElse,
|
|
2945
|
-
handleSubmit
|
|
2946
|
-
|
|
3362
|
+
handleSubmit: (...args) => selectionLocked ? Promise.resolve({
|
|
3363
|
+
ok: false,
|
|
3364
|
+
reason: "blocked",
|
|
3365
|
+
message: "This registration step cannot continue."
|
|
3366
|
+
}) : handleSubmit(...args),
|
|
3367
|
+
handleEventSelect: (value) => {
|
|
3368
|
+
if (!selectionLocked) handleEventSelect(value);
|
|
3369
|
+
},
|
|
2947
3370
|
handleRetry
|
|
2948
3371
|
};
|
|
2949
3372
|
}
|
|
2950
3373
|
//#endregion
|
|
2951
|
-
//#region ../blocks/src/system/runtime/nodes/event-registration/useEventCheckoutHandoffRestore.ts
|
|
2952
|
-
function eventRegistrationReloadTarget(location) {
|
|
2953
|
-
return `${location.pathname}${location.search}#${EVENT_REGISTRATION_ANCHOR_ID}`;
|
|
2954
|
-
}
|
|
2955
|
-
function reloadAtEventRegistration() {
|
|
2956
|
-
const target = eventRegistrationReloadTarget(window.location);
|
|
2957
|
-
window.history.replaceState(window.history.state, "", target);
|
|
2958
|
-
window.location.reload();
|
|
2959
|
-
}
|
|
2960
|
-
const browserCheckoutHandoffRestoreDeps = {
|
|
2961
|
-
addPageShowListener: (listener) => window.addEventListener("pageshow", listener),
|
|
2962
|
-
removePageShowListener: (listener) => window.removeEventListener("pageshow", listener),
|
|
2963
|
-
reloadAtRegistration: reloadAtEventRegistration
|
|
2964
|
-
};
|
|
2965
|
-
function useEventCheckoutHandoffRestore(active) {
|
|
2966
|
-
useEventCheckoutHandoffRestoreWithDeps(active, browserCheckoutHandoffRestoreDeps);
|
|
2967
|
-
}
|
|
2968
|
-
function useEventCheckoutHandoffRestoreWithDeps(active, deps) {
|
|
2969
|
-
useEffect(() => {
|
|
2970
|
-
if (!active) return;
|
|
2971
|
-
const handlePageShow = (event) => {
|
|
2972
|
-
if (event.persisted) deps.reloadAtRegistration();
|
|
2973
|
-
};
|
|
2974
|
-
deps.addPageShowListener(handlePageShow);
|
|
2975
|
-
return () => deps.removePageShowListener(handlePageShow);
|
|
2976
|
-
}, [active, deps]);
|
|
2977
|
-
}
|
|
2978
|
-
//#endregion
|
|
2979
3374
|
//#region ../blocks/src/system/runtime/nodes/event-registration/EventRegistrationBookingLayer.tsx
|
|
2980
3375
|
function BookingEntry(props) {
|
|
2981
3376
|
const base = props.variant === "teaser" ? "er-booking-teaser" : "er-booking-bar";
|
|
@@ -3577,49 +3972,23 @@ function EventRegistrationSummaryBar(props) {
|
|
|
3577
3972
|
*/
|
|
3578
3973
|
function resolveDetailsButtonText(input) {
|
|
3579
3974
|
const { wizard } = input;
|
|
3580
|
-
const
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3975
|
+
const admission = wizard.admission;
|
|
3976
|
+
switch (admission.kind) {
|
|
3977
|
+
case "checking": return "Checking availability...";
|
|
3978
|
+
case "closed": return admission.reason === "alreadyWaitlisted" ? "Already on waitlist" : "Registration unavailable";
|
|
3979
|
+
case "fullyBooked": return admission.recovery === "byEmail" ? "Continue" : "Sold out";
|
|
3980
|
+
case "waitlist": return admission.recovery === "byEmail" ? "Continue" : "Join waitlist";
|
|
3981
|
+
case "open":
|
|
3982
|
+
case "resume": break;
|
|
3983
|
+
}
|
|
3585
3984
|
if (wizard.paymentSelection.method === "pass") return "Book using class pass";
|
|
3586
3985
|
if (wizard.paymentSelection.method === "membership") return input.fallbackText;
|
|
3587
3986
|
return wizard.eventRequiresPayment ? "Continue to payment" : input.fallbackText;
|
|
3588
3987
|
}
|
|
3589
|
-
function resolveExistingRegistrationLabel(wizard) {
|
|
3590
|
-
return wizard.registrationState.kind === "waitlisted" ? "Already on waitlist" : "Already registered";
|
|
3591
|
-
}
|
|
3592
|
-
function isExistingRegistrationBlocking$1(wizard) {
|
|
3593
|
-
return hasBlockingExistingRegistration({
|
|
3594
|
-
isAuthenticated: wizard.portalAuthState === "authenticated",
|
|
3595
|
-
registration: wizard.registrationState,
|
|
3596
|
-
hasWaitlistClaim: wizard.waitlistClaim.claim !== null
|
|
3597
|
-
});
|
|
3598
|
-
}
|
|
3599
|
-
function resolveTicketSelectionContinueLabel(input) {
|
|
3600
|
-
if (input.isCheckingExistingRegistration) return "Checking registration...";
|
|
3601
|
-
if (input.ticketTypeSelectionBlocked) return "Choose a ticket";
|
|
3602
|
-
if (input.registrationOptionsPending) return "Checking availability...";
|
|
3603
|
-
if (input.registrationOptionsFailed) return "Availability unavailable";
|
|
3604
|
-
if (input.hasExistingRegistration) return resolveExistingRegistrationLabel(input.wizard);
|
|
3605
|
-
return "Continue";
|
|
3606
|
-
}
|
|
3607
3988
|
function SelectTicketsActions(props) {
|
|
3608
3989
|
const { wizard } = props;
|
|
3609
|
-
const
|
|
3610
|
-
const
|
|
3611
|
-
const registrationOptionsFailed = wizard.registrationOptionsState.tag === "failed";
|
|
3612
|
-
const isCheckingExistingRegistration = wizard.portalAuthState === "authenticated" && wizard.existingRegistrationLoading;
|
|
3613
|
-
const hasExistingRegistration = isExistingRegistrationBlocking$1(wizard);
|
|
3614
|
-
const isContinueDisabled = registrationOptionsPending || registrationOptionsFailed || isCheckingExistingRegistration || hasExistingRegistration || ticketTypeSelectionBlocked;
|
|
3615
|
-
const continueLabel = resolveTicketSelectionContinueLabel({
|
|
3616
|
-
wizard,
|
|
3617
|
-
registrationOptionsPending,
|
|
3618
|
-
registrationOptionsFailed,
|
|
3619
|
-
isCheckingExistingRegistration,
|
|
3620
|
-
hasExistingRegistration,
|
|
3621
|
-
ticketTypeSelectionBlocked
|
|
3622
|
-
});
|
|
3990
|
+
const { label: continueLabel, pending: registrationOptionsPending } = wizard.ticketContinuation;
|
|
3991
|
+
const isContinueDisabled = wizard.ticketContinuation.kind === "blocked";
|
|
3623
3992
|
return /* @__PURE__ */ jsxs("div", {
|
|
3624
3993
|
className: "rb-wizard-actions er-actions",
|
|
3625
3994
|
children: [!wizard.hasPreSelectedContext && /* @__PURE__ */ jsx("button", {
|
|
@@ -3629,7 +3998,7 @@ function SelectTicketsActions(props) {
|
|
|
3629
3998
|
children: "← Back"
|
|
3630
3999
|
}), /* @__PURE__ */ jsx(ThemedButton, {
|
|
3631
4000
|
type: "button",
|
|
3632
|
-
onClick:
|
|
4001
|
+
onClick: wizard.handleTicketsComplete,
|
|
3633
4002
|
disabled: isContinueDisabled,
|
|
3634
4003
|
variant: props.buttonVariant,
|
|
3635
4004
|
size: "md",
|
|
@@ -3645,18 +4014,11 @@ function SelectTicketsActions(props) {
|
|
|
3645
4014
|
}
|
|
3646
4015
|
function EnterDetailsActions(props) {
|
|
3647
4016
|
const { wizard } = props;
|
|
3648
|
-
const hasWaitlistClaim = wizard.waitlistClaim.claim !== null;
|
|
3649
4017
|
const isCheckingEligibility = wizard.eligibilityLoading;
|
|
3650
|
-
const eligibilityUnavailable = Boolean(resolveRegistrationAvailabilityBlocker(
|
|
3651
|
-
|
|
3652
|
-
eligibility: wizard.eligibility,
|
|
3653
|
-
eligibilityError: wizard.eligibilityError
|
|
3654
|
-
}));
|
|
3655
|
-
const isSoldOut = !hasWaitlistClaim && wizard.portalAuthState === "authenticated" && wizard.eligibility?.registration.kind !== "pending_payment" && isSelectionSoldOut(wizard.eligibility?.availability);
|
|
3656
|
-
const isContinueDisabled = isCheckingEligibility || eligibilityUnavailable || isSoldOut;
|
|
4018
|
+
const eligibilityUnavailable = Boolean(resolveRegistrationAvailabilityBlocker(wizard.admission));
|
|
4019
|
+
const isContinueDisabled = isCheckingEligibility || eligibilityUnavailable;
|
|
3657
4020
|
const continueLabel = isCheckingEligibility ? "Checking availability..." : wizard.eligibilityError ? "Availability unavailable" : eligibilityUnavailable ? "Registration unavailable" : resolveDetailsButtonText({
|
|
3658
4021
|
wizard,
|
|
3659
|
-
hasWaitlistClaim,
|
|
3660
4022
|
fallbackText: props.buttonText
|
|
3661
4023
|
});
|
|
3662
4024
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -3699,10 +4061,11 @@ function SelectPaymentActions(props) {
|
|
|
3699
4061
|
function EventRegistrationWizardActions(props) {
|
|
3700
4062
|
const { wizard } = props;
|
|
3701
4063
|
switch (wizard.step) {
|
|
4064
|
+
case "fully-booked":
|
|
3702
4065
|
case "error":
|
|
3703
4066
|
case "success":
|
|
3704
4067
|
case "submitting":
|
|
3705
|
-
case "
|
|
4068
|
+
case "payment":
|
|
3706
4069
|
case "verifying-payment":
|
|
3707
4070
|
case "verifying-timeout":
|
|
3708
4071
|
case "payment-status":
|
|
@@ -3715,6 +4078,118 @@ function EventRegistrationWizardActions(props) {
|
|
|
3715
4078
|
}
|
|
3716
4079
|
}
|
|
3717
4080
|
//#endregion
|
|
4081
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/EventRegistrationPayment.tsx
|
|
4082
|
+
const Payment = lazy(() => import("./EventEmbeddedCheckoutStep-DL6zFAkA.mjs"));
|
|
4083
|
+
/** The one Stripe mount, shared by the standard wizard and headless arrangements. */
|
|
4084
|
+
function EventPaymentMount({ state, onComplete, onRetry, onBack }) {
|
|
4085
|
+
if (state.kind === "abandoning") return /* @__PURE__ */ jsx("p", {
|
|
4086
|
+
role: "status",
|
|
4087
|
+
children: "Returning to booking…"
|
|
4088
|
+
});
|
|
4089
|
+
if (state.kind !== "payment") return null;
|
|
4090
|
+
return /* @__PURE__ */ jsx(Suspense, {
|
|
4091
|
+
fallback: /* @__PURE__ */ jsx("p", {
|
|
4092
|
+
role: "status",
|
|
4093
|
+
children: "Loading secure payment…"
|
|
4094
|
+
}),
|
|
4095
|
+
children: /* @__PURE__ */ jsx(Payment, {
|
|
4096
|
+
checkout: state.checkout,
|
|
4097
|
+
onComplete,
|
|
4098
|
+
onRetry,
|
|
4099
|
+
onBack: () => void onBack()
|
|
4100
|
+
})
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
//#endregion
|
|
4104
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/FullyBookedStep.tsx
|
|
4105
|
+
function RecoveryEmailField({ wizard }) {
|
|
4106
|
+
const id = useId();
|
|
4107
|
+
const errorId = `${id}-error`;
|
|
4108
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4109
|
+
className: "er-field",
|
|
4110
|
+
children: [
|
|
4111
|
+
/* @__PURE__ */ jsx("label", {
|
|
4112
|
+
className: "er-label",
|
|
4113
|
+
htmlFor: id,
|
|
4114
|
+
children: "Email"
|
|
4115
|
+
}),
|
|
4116
|
+
/* @__PURE__ */ jsx("input", {
|
|
4117
|
+
id,
|
|
4118
|
+
type: "email",
|
|
4119
|
+
autoComplete: "email",
|
|
4120
|
+
className: "er-input",
|
|
4121
|
+
value: wizard.formData.email,
|
|
4122
|
+
onChange: (event) => wizard.setFormData({
|
|
4123
|
+
...wizard.formData,
|
|
4124
|
+
email: event.target.value
|
|
4125
|
+
}),
|
|
4126
|
+
...getFieldValidationControlProps({
|
|
4127
|
+
message: wizard.errors.email,
|
|
4128
|
+
messageId: errorId
|
|
4129
|
+
})
|
|
4130
|
+
}),
|
|
4131
|
+
wizard.errors.email && /* @__PURE__ */ jsx(FieldValidationMessage, {
|
|
4132
|
+
id: errorId,
|
|
4133
|
+
className: "er-error",
|
|
4134
|
+
children: wizard.errors.email
|
|
4135
|
+
})
|
|
4136
|
+
]
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
function RecoveryEmail(props) {
|
|
4140
|
+
const { wizard } = props;
|
|
4141
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4142
|
+
className: "rb-reg-stack er-stack",
|
|
4143
|
+
children: [
|
|
4144
|
+
/* @__PURE__ */ jsx("p", {
|
|
4145
|
+
className: "er-text",
|
|
4146
|
+
children: "Already started a booking? Enter your email to finish paying."
|
|
4147
|
+
}),
|
|
4148
|
+
/* @__PURE__ */ jsx(RecoveryEmailField, { wizard }),
|
|
4149
|
+
props.showSpamProtection && /* @__PURE__ */ jsx("div", {
|
|
4150
|
+
className: "er-altcha",
|
|
4151
|
+
children: /* @__PURE__ */ jsx(AltchaWidget, {
|
|
4152
|
+
challengeUrl: getChallengeUrl(),
|
|
4153
|
+
onSolutionChange: wizard.setAltchaSolution
|
|
4154
|
+
})
|
|
4155
|
+
}),
|
|
4156
|
+
wizard.recoveryMessage && /* @__PURE__ */ jsx("p", {
|
|
4157
|
+
role: "alert",
|
|
4158
|
+
className: "er-error",
|
|
4159
|
+
children: wizard.recoveryMessage
|
|
4160
|
+
}),
|
|
4161
|
+
/* @__PURE__ */ jsx("button", {
|
|
4162
|
+
type: "button",
|
|
4163
|
+
className: "er-btn er-btn--primary",
|
|
4164
|
+
disabled: wizard.checkingNextStep || props.showSpamProtection && !wizard.altchaSolution,
|
|
4165
|
+
onClick: () => void wizard.handleRecoveryEmail(),
|
|
4166
|
+
children: wizard.checkingNextStep ? "Checking booking..." : "Continue"
|
|
4167
|
+
})
|
|
4168
|
+
]
|
|
4169
|
+
});
|
|
4170
|
+
}
|
|
4171
|
+
function FullyBookedStep(props) {
|
|
4172
|
+
const { wizard } = props;
|
|
4173
|
+
const admission = wizard.admission;
|
|
4174
|
+
const byEmail = admission.kind === "fullyBooked" && admission.recovery === "byEmail";
|
|
4175
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4176
|
+
className: "rb-reg-stack er-stack",
|
|
4177
|
+
children: [
|
|
4178
|
+
/* @__PURE__ */ jsx("h3", {
|
|
4179
|
+
className: "er-title",
|
|
4180
|
+
children: "This event is fully booked"
|
|
4181
|
+
}),
|
|
4182
|
+
byEmail && /* @__PURE__ */ jsx(RecoveryEmail, { ...props }),
|
|
4183
|
+
/* @__PURE__ */ jsx("button", {
|
|
4184
|
+
type: "button",
|
|
4185
|
+
className: "er-back-btn",
|
|
4186
|
+
onClick: () => wizard.setStep("select-tickets"),
|
|
4187
|
+
children: "← Back"
|
|
4188
|
+
})
|
|
4189
|
+
]
|
|
4190
|
+
});
|
|
4191
|
+
}
|
|
4192
|
+
//#endregion
|
|
3718
4193
|
//#region ../blocks/src/system/runtime/nodes/event-registration/siteCreditPreview.ts
|
|
3719
4194
|
function calculateEventRegistrationPreviewTotal(input) {
|
|
3720
4195
|
const discountCents = input.manualDiscountCode.trim() ? 0 : input.automaticDiscount?.computedAmountCents ?? 0;
|
|
@@ -3790,7 +4265,7 @@ function buildSelectedSiteCreditPreview(input) {
|
|
|
3790
4265
|
function resolvePaymentStepStatus(input) {
|
|
3791
4266
|
if (input.loading) return "loading";
|
|
3792
4267
|
if (input.error || !input.eligibility) return "error";
|
|
3793
|
-
if (input.
|
|
4268
|
+
if (resolveRegistrationAvailabilityBlocker(input.admission) !== null || input.eligibility.paymentOptions.length === 0) return "not-eligible";
|
|
3794
4269
|
return "ready";
|
|
3795
4270
|
}
|
|
3796
4271
|
function shouldPromptForLogin(status, isAuthenticated, eligibility) {
|
|
@@ -3824,8 +4299,8 @@ function UnavailableEntitlementMessage(props) {
|
|
|
3824
4299
|
})
|
|
3825
4300
|
] });
|
|
3826
4301
|
}
|
|
3827
|
-
function
|
|
3828
|
-
|
|
4302
|
+
function SoldOutPaymentPanel() {
|
|
4303
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3829
4304
|
className: "rb-reg-stack er-stack",
|
|
3830
4305
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
3831
4306
|
className: "er-title",
|
|
@@ -3838,6 +4313,8 @@ function IneligiblePaymentPanel(props) {
|
|
|
3838
4313
|
})
|
|
3839
4314
|
})]
|
|
3840
4315
|
});
|
|
4316
|
+
}
|
|
4317
|
+
function IneligiblePaymentPanel(props) {
|
|
3841
4318
|
const promptLogin = shouldPromptForLogin("not-eligible", props.isAuthenticated, props.eligibility);
|
|
3842
4319
|
return /* @__PURE__ */ jsxs("div", {
|
|
3843
4320
|
className: "rb-reg-stack er-stack",
|
|
@@ -3867,6 +4344,12 @@ function IneligiblePaymentPanel(props) {
|
|
|
3867
4344
|
})]
|
|
3868
4345
|
});
|
|
3869
4346
|
}
|
|
4347
|
+
function renderRegistrationAvailabilityBlocker(admission) {
|
|
4348
|
+
const blocker = resolveRegistrationAvailabilityBlocker(admission);
|
|
4349
|
+
if (blocker?.kind !== "unavailable") return null;
|
|
4350
|
+
if (admission.kind === "closed" && admission.reason === "alreadyWaitlisted") return /* @__PURE__ */ jsx(AlreadyWaitlistedPaymentPanel, {});
|
|
4351
|
+
return admission.kind === "fullyBooked" ? /* @__PURE__ */ jsx(SoldOutPaymentPanel, {}) : /* @__PURE__ */ jsx(ClosedPaymentPanel, { message: blocker.message });
|
|
4352
|
+
}
|
|
3870
4353
|
function PaymentBlockingPanel(props) {
|
|
3871
4354
|
if (props.eligibilityLoading) return /* @__PURE__ */ jsx("div", {
|
|
3872
4355
|
className: "rb-reg-stack er-stack",
|
|
@@ -3892,7 +4375,18 @@ function PaymentBlockingPanel(props) {
|
|
|
3892
4375
|
})
|
|
3893
4376
|
});
|
|
3894
4377
|
if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {});
|
|
3895
|
-
|
|
4378
|
+
const availabilityPanel = renderRegistrationAvailabilityBlocker(props.admission);
|
|
4379
|
+
if (availabilityPanel) return availabilityPanel;
|
|
4380
|
+
if (props.eligibility.isFree) return /* @__PURE__ */ jsx(FreePaymentSummary, { ticketsCount: props.ticketsCount });
|
|
4381
|
+
return props.eligibility.paymentOptions.length > 0 ? null : /* @__PURE__ */ jsx(IneligiblePaymentPanel, {
|
|
4382
|
+
eligibility: props.eligibility,
|
|
4383
|
+
email: props.email,
|
|
4384
|
+
isAuthenticated: props.isAuthenticated,
|
|
4385
|
+
onRequestLogin: props.onRequestLogin
|
|
4386
|
+
});
|
|
4387
|
+
}
|
|
4388
|
+
function AlreadyWaitlistedPaymentPanel() {
|
|
4389
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3896
4390
|
className: "rb-reg-stack er-stack",
|
|
3897
4391
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
3898
4392
|
className: "er-title",
|
|
@@ -3909,7 +4403,9 @@ function PaymentBlockingPanel(props) {
|
|
|
3909
4403
|
})]
|
|
3910
4404
|
})]
|
|
3911
4405
|
});
|
|
3912
|
-
|
|
4406
|
+
}
|
|
4407
|
+
function FreePaymentSummary({ ticketsCount }) {
|
|
4408
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3913
4409
|
className: "rb-reg-stack er-stack",
|
|
3914
4410
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
3915
4411
|
className: "er-title",
|
|
@@ -3922,18 +4418,24 @@ function PaymentBlockingPanel(props) {
|
|
|
3922
4418
|
}), /* @__PURE__ */ jsxs("p", {
|
|
3923
4419
|
className: "er-text er-text--muted",
|
|
3924
4420
|
children: [
|
|
3925
|
-
|
|
4421
|
+
ticketsCount,
|
|
3926
4422
|
" ticket",
|
|
3927
|
-
|
|
4423
|
+
ticketsCount > 1 ? "s" : ""
|
|
3928
4424
|
]
|
|
3929
4425
|
})]
|
|
3930
4426
|
})]
|
|
3931
4427
|
});
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
4428
|
+
}
|
|
4429
|
+
function ClosedPaymentPanel({ message }) {
|
|
4430
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4431
|
+
className: "rb-reg-stack er-stack",
|
|
4432
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
4433
|
+
className: "er-center",
|
|
4434
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
4435
|
+
className: "er-text er-text--error",
|
|
4436
|
+
children: message
|
|
4437
|
+
})
|
|
4438
|
+
})
|
|
3937
4439
|
});
|
|
3938
4440
|
}
|
|
3939
4441
|
//#endregion
|
|
@@ -4919,8 +5421,9 @@ function canContinueWithPaymentSelection(selection, directPaymentSelection) {
|
|
|
4919
5421
|
case null: return false;
|
|
4920
5422
|
}
|
|
4921
5423
|
}
|
|
4922
|
-
function
|
|
4923
|
-
|
|
5424
|
+
function buildEventPaymentSelectionView(input) {
|
|
5425
|
+
const canShowDirectPaymentOptions = Boolean(input.eligibility?.requiresPayment && input.eligibility.price);
|
|
5426
|
+
const directPaymentOptions = input.eligibility && canShowDirectPaymentOptions ? buildEventRegistrationPaymentOptionsViewModel({
|
|
4924
5427
|
eventTitle: input.event?.title ?? "Event",
|
|
4925
5428
|
approvedPaymentOptions: input.eligibility.paymentOptions,
|
|
4926
5429
|
paymentTerms: input.eligibility.paymentTerms,
|
|
@@ -4928,7 +5431,18 @@ function buildEventDirectPaymentOptions(input) {
|
|
|
4928
5431
|
currency: input.eligibility.currency,
|
|
4929
5432
|
startsAt: getRegistrationStartAt(input.event, input.occurrenceContext),
|
|
4930
5433
|
nowIso: input.nowIso
|
|
4931
|
-
}).options;
|
|
5434
|
+
}).options : [];
|
|
5435
|
+
const directPaymentSelectionState = resolveEventRegistrationDirectPaymentSelection({
|
|
5436
|
+
options: directPaymentOptions,
|
|
5437
|
+
selection: input.selection
|
|
5438
|
+
});
|
|
5439
|
+
return {
|
|
5440
|
+
canShowDirectPaymentOptions,
|
|
5441
|
+
directPaymentOptions,
|
|
5442
|
+
directPaymentSelectionState,
|
|
5443
|
+
directPaymentFallbackKind: directPaymentSelectionState.kind === "invalid" ? directPaymentSelectionState.fallbackSelection.collectionOptionKind : void 0,
|
|
5444
|
+
selectedDirectOption: directPaymentSelectionState.kind === "valid" ? directPaymentOptions.find((option) => option.kind === directPaymentSelectionState.selection.collectionOptionKind) ?? null : null
|
|
5445
|
+
};
|
|
4932
5446
|
}
|
|
4933
5447
|
/**
|
|
4934
5448
|
* Payment Selection Step
|
|
@@ -4937,24 +5451,17 @@ function buildEventDirectPaymentOptions(input) {
|
|
|
4937
5451
|
* Fetches eligible passes/memberships using the user's email.
|
|
4938
5452
|
* Also shows available passes/memberships for purchase (upsell).
|
|
4939
5453
|
*/
|
|
4940
|
-
const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, isAuthenticated = false, creditState = { kind: "not_authenticated" }, directPaymentTotalCents, manualDiscountPending = false, selectedSelection, onSelectionChange, eligibility, eligibilityLoading, eligibilityError = null, onRequestLogin, onStateChange, nowIso }) => {
|
|
5454
|
+
const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, isAuthenticated = false, creditState = { kind: "not_authenticated" }, directPaymentTotalCents, manualDiscountPending = false, selectedSelection, onSelectionChange, eligibility, admission, eligibilityLoading, eligibilityError = null, onRequestLogin, onStateChange, nowIso }) => {
|
|
4941
5455
|
const occurrenceId = occurrenceContext?.id ?? (event?.registrationSubject.kind === "occurrence" ? event.registrationSubject.occurrenceId : "");
|
|
4942
5456
|
const selectedMethod = selectedSelection.method;
|
|
4943
|
-
const
|
|
4944
|
-
const canShowDirectPaymentOptions = Boolean(eligibility?.requiresPayment && eligibility.price);
|
|
4945
|
-
const directPaymentOptions = eligibility && canShowDirectPaymentOptions ? buildEventDirectPaymentOptions({
|
|
5457
|
+
const { canShowDirectPaymentOptions, directPaymentOptions, directPaymentSelectionState, directPaymentFallbackKind, selectedDirectOption } = buildEventPaymentSelectionView({
|
|
4946
5458
|
event,
|
|
4947
5459
|
occurrenceContext,
|
|
4948
5460
|
eligibility,
|
|
4949
|
-
totalPriceCents:
|
|
4950
|
-
nowIso
|
|
4951
|
-
}) : [];
|
|
4952
|
-
const directPaymentSelectionState = resolveEventRegistrationDirectPaymentSelection({
|
|
4953
|
-
options: directPaymentOptions,
|
|
5461
|
+
totalPriceCents: directPaymentTotalCents,
|
|
5462
|
+
nowIso,
|
|
4954
5463
|
selection: selectedSelection
|
|
4955
5464
|
});
|
|
4956
|
-
const directPaymentFallbackKind = (directPaymentSelectionState.kind === "invalid" ? directPaymentSelectionState.fallbackSelection : null)?.collectionOptionKind;
|
|
4957
|
-
const selectedDirectOption = directPaymentSelectionState.kind === "valid" ? directPaymentOptions.find((option) => option.kind === directPaymentSelectionState.selection.collectionOptionKind) ?? null : null;
|
|
4958
5465
|
const siteCreditPreview = buildSelectedSiteCreditPreview({
|
|
4959
5466
|
options: directPaymentOptions,
|
|
4960
5467
|
selectionState: directPaymentSelectionState,
|
|
@@ -4973,7 +5480,8 @@ const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, i
|
|
|
4973
5480
|
const paymentStepStatus = resolvePaymentStepStatus({
|
|
4974
5481
|
loading: eligibilityLoading,
|
|
4975
5482
|
error: eligibilityError,
|
|
4976
|
-
eligibility
|
|
5483
|
+
eligibility,
|
|
5484
|
+
admission
|
|
4977
5485
|
});
|
|
4978
5486
|
const paymentButtonText = resolvePaymentButtonText(selectedMethod, siteCreditPreview, eligibility, selectedDirectOption);
|
|
4979
5487
|
const showLoginPrompt = shouldPromptForLogin(paymentStepStatus, isAuthenticated, eligibility);
|
|
@@ -4994,6 +5502,7 @@ const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, i
|
|
|
4994
5502
|
]);
|
|
4995
5503
|
const blockingPanel = /* @__PURE__ */ jsx(PaymentBlockingPanel, {
|
|
4996
5504
|
eligibility,
|
|
5505
|
+
admission,
|
|
4997
5506
|
eligibilityLoading,
|
|
4998
5507
|
eligibilityError,
|
|
4999
5508
|
occurrenceId,
|
|
@@ -5141,16 +5650,6 @@ function ExistingRegistrationNotice(props) {
|
|
|
5141
5650
|
})
|
|
5142
5651
|
});
|
|
5143
5652
|
}
|
|
5144
|
-
function resolveExistingRegistrationKind(wizard) {
|
|
5145
|
-
return wizard.registrationState.kind === "waitlisted" ? "waitlisted" : "booked";
|
|
5146
|
-
}
|
|
5147
|
-
function isExistingRegistrationBlocking(wizard) {
|
|
5148
|
-
return hasBlockingExistingRegistration({
|
|
5149
|
-
isAuthenticated: wizard.portalAuthState === "authenticated",
|
|
5150
|
-
registration: wizard.registrationState,
|
|
5151
|
-
hasWaitlistClaim: wizard.waitlistClaim.claim !== null
|
|
5152
|
-
});
|
|
5153
|
-
}
|
|
5154
5653
|
function AttendeeDetailsContent(props) {
|
|
5155
5654
|
const { wizard } = props;
|
|
5156
5655
|
const membership = wizard.portalAuthState === "authenticated" && wizard.ticketsCount === 1 ? wizard.eligibility?.eligibleMemberships[0] : void 0;
|
|
@@ -5203,6 +5702,7 @@ function PaymentSelectionContent(props) {
|
|
|
5203
5702
|
if (!wizard.api.config.siteId) return null;
|
|
5204
5703
|
if (subtotalCents === null) return /* @__PURE__ */ jsx(PaymentBlockingPanel, {
|
|
5205
5704
|
eligibility: wizard.eligibility,
|
|
5705
|
+
admission: wizard.admission,
|
|
5206
5706
|
eligibilityLoading: wizard.eligibilityLoading,
|
|
5207
5707
|
eligibilityError: wizard.eligibilityError,
|
|
5208
5708
|
occurrenceId: wizard.occurrenceContext?.id ?? "",
|
|
@@ -5227,6 +5727,7 @@ function PaymentSelectionContent(props) {
|
|
|
5227
5727
|
selectedSelection: wizard.paymentSelection,
|
|
5228
5728
|
onSelectionChange: wizard.handlePaymentSelectionChange,
|
|
5229
5729
|
eligibility: wizard.eligibility,
|
|
5730
|
+
admission: wizard.admission,
|
|
5230
5731
|
eligibilityLoading: wizard.eligibilityLoading,
|
|
5231
5732
|
eligibilityError: wizard.eligibilityError,
|
|
5232
5733
|
onRequestLogin: () => wizard.setShowMagicLinkModal(true),
|
|
@@ -5234,14 +5735,54 @@ function PaymentSelectionContent(props) {
|
|
|
5234
5735
|
nowIso: wizard.nowIso
|
|
5235
5736
|
}), /* @__PURE__ */ jsx(EventDiscountCodeDisclosure, { wizard })] });
|
|
5236
5737
|
}
|
|
5738
|
+
function ClosedAdmissionNotice({ wizard }) {
|
|
5739
|
+
const admission = wizard.admission;
|
|
5740
|
+
return admission.kind === "closed" && admission.reason !== "alreadyWaitlisted" && !wizard.eligibilityLoading ? /* @__PURE__ */ jsx("p", {
|
|
5741
|
+
role: "alert",
|
|
5742
|
+
className: "er-error",
|
|
5743
|
+
children: admission.message
|
|
5744
|
+
}) : null;
|
|
5745
|
+
}
|
|
5746
|
+
function TicketSelectionContent({ wizard }) {
|
|
5747
|
+
if (!wizard.selectedEvent && !wizard.occurrenceContext) return null;
|
|
5748
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5749
|
+
wizard.admission.kind === "closed" && wizard.admission.reason === "alreadyWaitlisted" && /* @__PURE__ */ jsx(ExistingRegistrationNotice, {
|
|
5750
|
+
kind: "waitlisted",
|
|
5751
|
+
onBookForSomeoneElse: wizard.handleBookForSomeoneElse
|
|
5752
|
+
}),
|
|
5753
|
+
/* @__PURE__ */ jsx(ClosedAdmissionNotice, { wizard }),
|
|
5754
|
+
/* @__PURE__ */ jsx(TicketSelectionStep, {
|
|
5755
|
+
event: wizard.selectedEvent,
|
|
5756
|
+
occurrenceContext: wizard.occurrenceContext,
|
|
5757
|
+
selectedAttendanceMode: wizard.selectedAttendanceMode,
|
|
5758
|
+
onAttendanceModeChange: wizard.setSelectedAttendanceMode,
|
|
5759
|
+
ticketsCount: wizard.ticketsCount,
|
|
5760
|
+
maxTickets: wizard.maxTicketsNum,
|
|
5761
|
+
visibleTicketTypes: wizard.visibleTicketTypes,
|
|
5762
|
+
ticketSelections: wizard.selectedTicketLines,
|
|
5763
|
+
onTicketSelectionsChange: wizard.setSelectedTicketLines,
|
|
5764
|
+
slidingScale: wizard.selectedEventSlidingScale,
|
|
5765
|
+
selectedSlidingAmountInput: wizard.selectedSlidingAmountInput,
|
|
5766
|
+
slidingScaleSelectionMessage: wizard.slidingScaleSelectionMessage,
|
|
5767
|
+
onSlidingAmountChange: wizard.setSelectedSlidingAmountInput,
|
|
5768
|
+
onTicketsChange: wizard.setTicketsCount,
|
|
5769
|
+
bookingUnit: resolveBookingUnit(wizard.selectedEvent)
|
|
5770
|
+
})
|
|
5771
|
+
] });
|
|
5772
|
+
}
|
|
5237
5773
|
function EventRegistrationWizardContent(props) {
|
|
5238
5774
|
const { wizard } = props;
|
|
5239
5775
|
const showSpamProtection = props.showSpamProtection ?? isSpamProtectionEnabled();
|
|
5240
5776
|
switch (wizard.step) {
|
|
5777
|
+
case "payment": return /* @__PURE__ */ jsx(EventPaymentMount, {
|
|
5778
|
+
state: wizard.paymentController.state,
|
|
5779
|
+
onComplete: wizard.paymentController.check,
|
|
5780
|
+
onRetry: wizard.paymentController.retryPaymentLoad,
|
|
5781
|
+
onBack: wizard.paymentController.backToBooking
|
|
5782
|
+
});
|
|
5241
5783
|
case "error":
|
|
5242
5784
|
case "success":
|
|
5243
5785
|
case "submitting":
|
|
5244
|
-
case "redirecting":
|
|
5245
5786
|
case "verifying-payment":
|
|
5246
5787
|
case "verifying-timeout":
|
|
5247
5788
|
case "payment-status":
|
|
@@ -5253,28 +5794,11 @@ function EventRegistrationWizardContent(props) {
|
|
|
5253
5794
|
showVenue: props.showVenue,
|
|
5254
5795
|
showCapacity: props.showCapacity
|
|
5255
5796
|
});
|
|
5256
|
-
case "select-tickets":
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
}), /* @__PURE__ */ jsx(TicketSelectionStep, {
|
|
5262
|
-
event: wizard.selectedEvent,
|
|
5263
|
-
occurrenceContext: wizard.occurrenceContext,
|
|
5264
|
-
selectedAttendanceMode: wizard.selectedAttendanceMode,
|
|
5265
|
-
onAttendanceModeChange: wizard.setSelectedAttendanceMode,
|
|
5266
|
-
ticketsCount: wizard.ticketsCount,
|
|
5267
|
-
maxTickets: wizard.maxTicketsNum,
|
|
5268
|
-
visibleTicketTypes: wizard.visibleTicketTypes,
|
|
5269
|
-
ticketSelections: wizard.selectedTicketLines,
|
|
5270
|
-
onTicketSelectionsChange: wizard.setSelectedTicketLines,
|
|
5271
|
-
slidingScale: wizard.selectedEventSlidingScale,
|
|
5272
|
-
selectedSlidingAmountInput: wizard.selectedSlidingAmountInput,
|
|
5273
|
-
slidingScaleSelectionMessage: wizard.slidingScaleSelectionMessage,
|
|
5274
|
-
onSlidingAmountChange: wizard.setSelectedSlidingAmountInput,
|
|
5275
|
-
onTicketsChange: wizard.setTicketsCount,
|
|
5276
|
-
bookingUnit: resolveBookingUnit(wizard.selectedEvent)
|
|
5277
|
-
})] });
|
|
5797
|
+
case "select-tickets": return /* @__PURE__ */ jsx(TicketSelectionContent, { wizard });
|
|
5798
|
+
case "fully-booked": return /* @__PURE__ */ jsx(FullyBookedStep, {
|
|
5799
|
+
wizard,
|
|
5800
|
+
showSpamProtection
|
|
5801
|
+
});
|
|
5278
5802
|
case "enter-details": return /* @__PURE__ */ jsx(AttendeeDetailsContent, {
|
|
5279
5803
|
wizard,
|
|
5280
5804
|
showSpamProtection
|
|
@@ -5353,7 +5877,10 @@ function presentation(state) {
|
|
|
5353
5877
|
case "assistanceRequired": return {
|
|
5354
5878
|
title: "We still can’t confirm your payment",
|
|
5355
5879
|
message: "Please don’t make another payment. Contact the organiser so they can check your booking.",
|
|
5356
|
-
action: {
|
|
5880
|
+
action: {
|
|
5881
|
+
label: "Return to booking",
|
|
5882
|
+
disabled: true
|
|
5883
|
+
}
|
|
5357
5884
|
};
|
|
5358
5885
|
}
|
|
5359
5886
|
}
|
|
@@ -5604,51 +6131,27 @@ function EventRegistrationMagicLinkModal(props) {
|
|
|
5604
6131
|
function EventRegistrationTerminalState(props) {
|
|
5605
6132
|
const { wizard } = props;
|
|
5606
6133
|
switch (wizard.step) {
|
|
5607
|
-
case "submitting":
|
|
5608
|
-
case "
|
|
5609
|
-
const label = wizard.step === "redirecting" ? "Redirecting to payment" : "Processing your registration";
|
|
5610
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
5611
|
-
className: "er-center",
|
|
5612
|
-
children: [/* @__PURE__ */ jsx(SpinnerNode, {
|
|
5613
|
-
size: "lg",
|
|
5614
|
-
srLabel: label
|
|
5615
|
-
}), /* @__PURE__ */ jsxs("p", {
|
|
5616
|
-
className: "er-text--muted",
|
|
5617
|
-
children: [label, "..."]
|
|
5618
|
-
})]
|
|
5619
|
-
});
|
|
5620
|
-
}
|
|
6134
|
+
case "submitting": return /* @__PURE__ */ jsx(RegistrationProcessing, {});
|
|
6135
|
+
case "payment": return null;
|
|
5621
6136
|
case "verifying-payment": return /* @__PURE__ */ jsx(VerifyingPaymentStep, {});
|
|
5622
6137
|
case "payment-failed": return /* @__PURE__ */ jsx(PaymentFailedStep, {
|
|
5623
6138
|
message: wizard.errorMessage,
|
|
5624
6139
|
onRetry: () => wizard.setStep("select-payment")
|
|
5625
6140
|
});
|
|
5626
|
-
case "verifying-timeout": return /* @__PURE__ */ jsx(
|
|
5627
|
-
|
|
5628
|
-
title: wizard.verification.status === "timeout" ? void 0 : "We’re checking your payment",
|
|
5629
|
-
message: wizard.verification.status === "timeout" ? void 0 : wizard.errorMessage,
|
|
6141
|
+
case "verifying-timeout": return /* @__PURE__ */ jsx(RegistrationVerificationTimeout, {
|
|
6142
|
+
wizard,
|
|
5630
6143
|
supportEmail: props.supportEmail
|
|
5631
6144
|
});
|
|
5632
|
-
case "payment-status": return
|
|
5633
|
-
state: wizard.paymentStatusRecovery,
|
|
5634
|
-
onCheck: wizard.handleCheckPaymentStatus,
|
|
5635
|
-
onContinuePayment: wizard.handleContinueExistingPayment,
|
|
5636
|
-
onReturnToPayment: wizard.handleReturnToPayment,
|
|
5637
|
-
onReturnToBooking: wizard.handleReturnToBooking
|
|
5638
|
-
}) : null;
|
|
6145
|
+
case "payment-status": return /* @__PURE__ */ jsx(RegistrationPaymentStatus, { wizard });
|
|
5639
6146
|
case "success": return /* @__PURE__ */ jsx(EventRegistrationSuccess, {
|
|
5640
6147
|
wizard,
|
|
5641
6148
|
fallbackMessage: props.successMessage,
|
|
5642
6149
|
demoEmailSuppressed: props.demoEmailSuppressed
|
|
5643
6150
|
});
|
|
5644
|
-
case "error": return /* @__PURE__ */ jsx(
|
|
5645
|
-
message: wizard.errorMessage,
|
|
5646
|
-
action: wizard.errorAction,
|
|
5647
|
-
canJoinWaitlist: wizard.eligibility?.availability.kind === "waitlist" && wizard.eligibility.registration.kind !== "pending_payment",
|
|
5648
|
-
onRetry: wizard.handleRetry
|
|
5649
|
-
});
|
|
6151
|
+
case "error": return /* @__PURE__ */ jsx(RegistrationError, { wizard });
|
|
5650
6152
|
case "select-event":
|
|
5651
6153
|
case "select-tickets":
|
|
6154
|
+
case "fully-booked":
|
|
5652
6155
|
case "enter-details":
|
|
5653
6156
|
case "select-payment": return null;
|
|
5654
6157
|
}
|
|
@@ -5717,6 +6220,46 @@ function EventRegistrationSurface(props) {
|
|
|
5717
6220
|
})]
|
|
5718
6221
|
});
|
|
5719
6222
|
}
|
|
6223
|
+
function RegistrationProcessing() {
|
|
6224
|
+
const label = "Processing your registration";
|
|
6225
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
6226
|
+
className: "er-center",
|
|
6227
|
+
children: [/* @__PURE__ */ jsx(SpinnerNode, {
|
|
6228
|
+
size: "lg",
|
|
6229
|
+
srLabel: label
|
|
6230
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
6231
|
+
className: "er-text--muted",
|
|
6232
|
+
children: [label, "..."]
|
|
6233
|
+
})]
|
|
6234
|
+
});
|
|
6235
|
+
}
|
|
6236
|
+
function RegistrationVerificationTimeout({ wizard, supportEmail }) {
|
|
6237
|
+
const timedOut = wizard.verification.status === "timeout";
|
|
6238
|
+
return /* @__PURE__ */ jsx(VerifyingTimeoutStep, {
|
|
6239
|
+
onCheckAgain: timedOut ? wizard.verification.retry : wizard.verification.retryReturn,
|
|
6240
|
+
title: timedOut ? void 0 : "We’re checking your payment",
|
|
6241
|
+
message: timedOut ? void 0 : wizard.errorMessage,
|
|
6242
|
+
supportEmail
|
|
6243
|
+
});
|
|
6244
|
+
}
|
|
6245
|
+
function RegistrationPaymentStatus({ wizard }) {
|
|
6246
|
+
return wizard.paymentStatusRecovery ? /* @__PURE__ */ jsx(PaymentStatusStep, {
|
|
6247
|
+
state: wizard.paymentStatusRecovery,
|
|
6248
|
+
onCheck: wizard.handleCheckPaymentStatus,
|
|
6249
|
+
onContinuePayment: wizard.handleContinueExistingPayment,
|
|
6250
|
+
onReturnToPayment: wizard.handleReturnToPayment,
|
|
6251
|
+
onReturnToBooking: wizard.handleReturnToBooking
|
|
6252
|
+
}) : null;
|
|
6253
|
+
}
|
|
6254
|
+
function RegistrationError({ wizard }) {
|
|
6255
|
+
const admission = wizard.admission;
|
|
6256
|
+
return /* @__PURE__ */ jsx(ErrorStep, {
|
|
6257
|
+
message: wizard.errorMessage,
|
|
6258
|
+
action: wizard.errorAction,
|
|
6259
|
+
canJoinWaitlist: admission.kind === "waitlist",
|
|
6260
|
+
onRetry: wizard.handleRetry
|
|
6261
|
+
});
|
|
6262
|
+
}
|
|
5720
6263
|
//#endregion
|
|
5721
6264
|
//#region ../blocks/src/system/runtime/nodes/event-registration/EventRegistrationWizard.tsx
|
|
5722
6265
|
const EVENT_PORTAL_AUTH_COPY = {
|
|
@@ -5764,11 +6307,12 @@ function getWizardStepLabel(input) {
|
|
|
5764
6307
|
case "select-event": return input.eventSelectionLabel;
|
|
5765
6308
|
case "select-tickets": return input.selectedEvent?.registrationSubject.kind === "wholeSeries" ? "Places" : "Tickets";
|
|
5766
6309
|
case "enter-details": return "Details";
|
|
5767
|
-
case "select-payment": return "Payment";
|
|
6310
|
+
case "select-payment": return "Payment options";
|
|
6311
|
+
case "payment": return "Payment";
|
|
6312
|
+
case "fully-booked":
|
|
5768
6313
|
case "error":
|
|
5769
6314
|
case "success":
|
|
5770
6315
|
case "submitting":
|
|
5771
|
-
case "redirecting":
|
|
5772
6316
|
case "verifying-payment":
|
|
5773
6317
|
case "verifying-timeout":
|
|
5774
6318
|
case "payment-status":
|
|
@@ -5799,7 +6343,6 @@ function EventRegistrationWizardView(viewProps) {
|
|
|
5799
6343
|
const showSpamProtection = spamProtectionEnabled ?? isSpamProtectionEnabled();
|
|
5800
6344
|
const registrationRootClassName = `event-registration ${className || ""}`;
|
|
5801
6345
|
const registrationRootRef = React.useRef(null);
|
|
5802
|
-
useEventCheckoutHandoffRestore(wizard.step === "redirecting");
|
|
5803
6346
|
const [bookingOverlayOpen, setBookingOverlayOpen] = useState(false);
|
|
5804
6347
|
const [floatingPortalRoot, setFloatingPortalRoot] = useState(null);
|
|
5805
6348
|
useEffect(() => {
|
|
@@ -5839,19 +6382,19 @@ function EventRegistrationWizardView(viewProps) {
|
|
|
5839
6382
|
})
|
|
5840
6383
|
})
|
|
5841
6384
|
});
|
|
5842
|
-
const isWizardStep = WIZARD_STEPS.includes(wizard.step);
|
|
5843
6385
|
const activeWizardSteps = useMemo(() => {
|
|
5844
6386
|
const needsPaymentStep = requiresPaymentStep({
|
|
5845
6387
|
eventRequiresPayment: wizard.eventRequiresPayment,
|
|
5846
6388
|
paymentSelection: wizard.paymentSelection
|
|
5847
6389
|
});
|
|
5848
|
-
return getWizardStepsForContext(wizard.hasPreSelectedContext, needsPaymentStep);
|
|
6390
|
+
return getWizardStepsForContext(wizard.hasPreSelectedContext, needsPaymentStep, wizard.step === "payment");
|
|
5849
6391
|
}, [
|
|
6392
|
+
wizard.step,
|
|
5850
6393
|
wizard.hasPreSelectedContext,
|
|
5851
6394
|
wizard.eventRequiresPayment,
|
|
5852
6395
|
wizard.paymentSelection
|
|
5853
6396
|
]);
|
|
5854
|
-
const totalSteps = activeWizardSteps
|
|
6397
|
+
const { isWizardStep, totalSteps } = getRegistrationStepPresentation(wizard.step, activeWizardSteps);
|
|
5855
6398
|
const summaryEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
|
|
5856
6399
|
const eventSelectionStepLabel = getEventSelectionMode(wizard.scopedEvents) === "date" ? "Date" : "Event";
|
|
5857
6400
|
const stepLabels = activeWizardSteps.map((step) => getWizardStepLabel({
|
|
@@ -5911,7 +6454,10 @@ function EventRegistrationWizardView(viewProps) {
|
|
|
5911
6454
|
ticketCurrency: bookingCurrency,
|
|
5912
6455
|
buttonVariant,
|
|
5913
6456
|
formatTicketPrice: (ticketType) => formatTicketPriceIfKnown(ticketType, bookingCurrency),
|
|
5914
|
-
onOpen: () =>
|
|
6457
|
+
onOpen: () => {
|
|
6458
|
+
if (wizard.paymentController.state.kind === "payment") wizard.paymentController.retryPaymentLoad();
|
|
6459
|
+
setBookingOverlayOpen(true);
|
|
6460
|
+
},
|
|
5915
6461
|
onClose: () => setBookingOverlayOpen(false),
|
|
5916
6462
|
overlaySurface: renderRegistrationSurface("overlay")
|
|
5917
6463
|
})]
|