@riverbankcms/sdk 0.136.0 → 0.137.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/CHANGELOG.md +101 -0
- package/dist/{PreviewEditorSidebar-BU5ZlhQR.mjs → PreviewEditorSidebar-Bn6cONPa.mjs} +8 -8
- package/dist/{PreviewEditorUI-9XkHL-bC.mjs → PreviewEditorUI-LAwXZPSx.mjs} +3 -3
- package/dist/{PreviewQueryProvider-wbEpXryy.mjs → PreviewQueryProvider-ztQUdsNo.mjs} +1 -1
- package/dist/{SdkPreviewModeRuntime-CnWmj-2B.mjs → SdkPreviewModeRuntime-BbbGsI_v.mjs} +596 -164
- package/dist/{SiteChromeCustomizeContext-B6d5zkog.mjs → SiteChromeCustomizeContext-BbkNSt5a.mjs} +2888 -1404
- package/dist/_dts/ai/src/contracts/proposals.d.ts +12 -298
- package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +21 -14
- package/dist/_dts/ai/src/designer/themePatch.d.ts +5 -10
- package/dist/_dts/api/src/accessAdmin.d.ts +1 -9
- package/dist/_dts/api/src/accountApi.d.ts +49 -2
- package/dist/_dts/api/src/contentRuntime.d.ts +1 -4
- package/dist/_dts/api/src/domainConnection.d.ts +41 -0
- package/dist/_dts/api/src/endpoints.d.ts +194 -181
- package/dist/_dts/api/src/index.d.ts +4 -3
- package/dist/_dts/api/src/media.d.ts +19 -2
- package/dist/_dts/api/src/sdkContracts.d.ts +2 -15
- package/dist/_dts/api/src/sdkMediaContracts.d.ts +40 -0
- package/dist/_dts/api/src/sdkMediaEndpointDefinitions.d.ts +61 -0
- package/dist/_dts/api/src/sdkThemeWire.d.ts +15 -4
- package/dist/_dts/api/src/siteInfrastructure.d.ts +17 -23
- package/dist/_dts/api/src/siteManagementEndpoints.d.ts +7 -21
- package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +27 -3
- package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +37 -17
- package/dist/_dts/api/src/types.d.ts +1 -1
- package/dist/_dts/blocks/src/PageRenderer.d.ts +2 -2
- package/dist/_dts/blocks/src/index.d.ts +1 -1
- package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +9 -0
- package/dist/_dts/blocks/src/pageRendererBlockSupport.d.ts +75 -0
- package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +2 -0
- package/dist/_dts/blocks/src/server.d.ts +1 -1
- package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +1 -1
- package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +2 -1
- package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +3 -3
- package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +34 -22
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ErrorStep.d.ts +3 -5
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +2 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.d.ts +5 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +25 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.d.ts +4 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +7 -3
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +2 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +5 -2
- package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +2 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/capacity.d.ts +164 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/cta.d.ts +29 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +47 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +14 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/people.d.ts +6 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/pricing.d.ts +31 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/OfferingTitle.d.ts +11 -0
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +1 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +1 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +3 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listingLayout.d.ts +23 -0
- package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +57 -0
- package/dist/_dts/commerce/src/purchase/index.d.ts +1 -0
- package/dist/_dts/db/src/generated/supabase/database.types.d.ts +8 -40
- package/dist/_dts/db/src/index.d.ts +1 -0
- package/dist/_dts/db/src/utils/postgrestFilter.d.ts +2 -0
- package/dist/_dts/media-core/src/bulkDeletePlanning.d.ts +4 -0
- package/dist/_dts/media-core/src/fileDomain.d.ts +3 -0
- package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +1 -0
- package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +1 -0
- package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +3 -2
- package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +1 -1
- package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +2 -0
- package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlSaveRequest.d.ts +51 -0
- package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +24 -2
- package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +23 -1
- package/dist/_dts/preview-next/src/client/preview/usePersistedHeaderPreferences.d.ts +8 -0
- package/dist/_dts/sdk/src/public-api/contracts.d.ts +2 -1
- package/dist/_dts/sdk/src/version.d.ts +1 -1
- package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
- package/dist/_dts/theme-core/src/gradients/types.d.ts +5 -0
- package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +25 -2
- package/dist/_dts/theme-core/src/mock-themes/all.d.ts +3 -3
- package/dist/_dts/theme-core/src/schema.d.ts +12 -11
- package/dist/_dts/theme-core/src/site-styles/compilationPlan.d.ts +43 -3
- package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +10 -0
- package/dist/_dts/theme-core/src/site-styles/headerBrandPresentation.d.ts +63 -0
- package/dist/_dts/theme-core/src/site-styles/headerBytesComparison.d.ts +30 -0
- package/dist/_dts/theme-core/src/site-styles/headerInteractionPalette.d.ts +50 -1
- package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +48 -9
- package/dist/_dts/theme-core/src/site-styles/headerPreferences.d.ts +162 -0
- package/dist/_dts/theme-core/src/site-styles/headerPreferencesPersistence.d.ts +73 -0
- package/dist/_dts/theme-core/src/site-styles/headerRecompile.d.ts +118 -0
- package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +10 -1
- package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +1 -1
- package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +7 -0
- package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
- package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +1 -1
- package/dist/_dts/theme-core/src/site-styles/siteChromeBackgrounds.d.ts +20 -1
- package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +43 -2
- package/dist/client/{BookingConsentAgreement-DCch21a6.mjs → BookingConsentAgreement--_fUCZe-.mjs} +2 -2
- package/dist/client/{DiscountCodeDisclosure-DIj1T59K.mjs → DiscountCodeDisclosure-BMipSoDJ.mjs} +1 -1
- package/dist/client/{EventCombined.client-BWVNlXTx.mjs → EventCombined.client-CwJOxn7A.mjs} +154 -178
- package/dist/client/{EventListing.client-DVFCDe3i.mjs → EventListing.client-eVwiylPW.mjs} +2 -2
- package/dist/client/{LeafletStaticMap-BY5mIHw9.mjs → LeafletStaticMap-IKenna5u.mjs} +1 -1
- package/dist/client/{accordion-CK29E8rG.mjs → accordion-MGAO3PuE.mjs} +1 -1
- package/dist/client/{bookingForm-DFvnet-N.mjs → bookingForm-CudlQPUu.mjs} +5 -5
- package/dist/client/bookings.mjs +418 -79
- package/dist/client/capacity-BW00PO7s.mjs +271 -0
- package/dist/client/{carousel-BVU5NQlb.mjs → carousel-BrbGJ2Jl.mjs} +1 -1
- package/dist/client/{cart-BNItcZVa.mjs → cart-Bq5eU0xZ.mjs} +3 -3
- package/dist/client/{checkout-Bu6iCdkP.mjs → checkout-BAFVRdrK.mjs} +3 -3
- package/dist/client/client.mjs +2919 -1918
- package/dist/client/{context-BImarOAq.mjs → context-DCkiHVdD.mjs} +8 -5
- package/dist/client/dates-2RPXHQy4.mjs +264 -0
- package/dist/client/{eventCalendar-DS2h-UXD.mjs → eventCalendar-B0O-hPF7.mjs} +10 -17
- package/dist/client/{eventCombined-Drt4Oq9p.mjs → eventCombined-DCkDaoDI.mjs} +3 -3
- package/dist/client/{eventDetails-Bl2vWgTX.mjs → eventDetails-D8GG2dnD.mjs} +5 -3
- package/dist/client/{eventListing-CS7PHY7v.mjs → eventListing-3ssV_bxz.mjs} +3 -3
- package/dist/client/{eventRegistration-D-j8eNy2.mjs → eventRegistration-bPQo3kVw.mjs} +562 -309
- package/dist/client/{form-RMpoLTyr.mjs → form-qMv8RDkk.mjs} +2 -2
- package/dist/client/{headerSection-BYE3Lb3k.mjs → headerSection-CH0Sc5hX.mjs} +2 -2
- package/dist/client/hooks.mjs +289 -32
- package/dist/client/{locationMap-BxSr7Kkh.mjs → locationMap-0_xlBnNI.mjs} +2 -2
- package/dist/client/{newsletterForm-DerbXwFC.mjs → newsletterForm-NUW7m9RI.mjs} +2 -2
- package/dist/client/{productDetail-4SZCLhAf.mjs → productDetail-FBEOM7jW.mjs} +3 -3
- package/dist/client/{productList-D7AP4GNq.mjs → productList-R0OSGhnn.mjs} +3 -3
- package/dist/client/{propsDecoder-pzBeCh14.mjs → propsDecoder-pYvx_Srl.mjs} +88 -380
- package/dist/client/rendering/client.mjs +2616 -1637
- package/dist/client/rendering/islands.mjs +17 -17
- package/dist/client/rendering.mjs +2710 -1702
- package/dist/client/{scrollToBlock-CduAKf8T.mjs → scrollToBlock-ByQv96M2.mjs} +1 -1
- package/dist/client/{shop-Cp5p_kGe.mjs → shop-RUG9DmRE.mjs} +6 -6
- package/dist/client/{shop-commerce-DicXYaYX.mjs → shop-commerce-B76Jz23B.mjs} +5 -5
- package/dist/client/{shop-commerce-store-CGWnpY4K.mjs → shop-commerce-store-QuQNdDsD.mjs} +1 -1
- package/dist/client/{shopShared-BDf9ljkm.mjs → shopShared-vXU1omwJ.mjs} +3 -3
- package/dist/client/{useEventFilters-DeeyGYuk.mjs → useEventFilters-ByuU6HLA.mjs} +33 -18
- package/dist/preview-next/before-render.mjs +79 -51
- package/dist/preview-next/client/runtime.mjs +4 -4
- package/dist/preview-next/keepAlive.mjs +79 -51
- package/dist/preview-next/middleware.mjs +79 -51
- package/dist/{previewAuthStore-DbqBol0V.mjs → previewAuthStore-L_cKW8Sb.mjs} +79 -51
- package/dist/{sdk-runtime-19MO27F0.mjs → sdk-runtime-sUMr5d70.mjs} +2 -2
- package/dist/{separator-DaPLtJ1f.mjs → separator-BIHkie5i.mjs} +1 -1
- package/dist/server/components.mjs +2077 -1274
- package/dist/server/config-validation.mjs +289 -32
- package/dist/server/config.mjs +289 -32
- package/dist/server/data.mjs +289 -32
- package/dist/server/index.mjs +80 -52
- package/dist/server/next.mjs +1822 -1111
- package/dist/server/page-converter.mjs +167 -31
- package/dist/server/prebuild.mjs +1 -1
- package/dist/server/rendering/server.mjs +1988 -1286
- package/dist/server/rendering.mjs +1943 -1182
- package/dist/server/routing.mjs +351 -86
- package/dist/server/server.mjs +293 -128
- package/dist/server/theme-bridge.mjs +52 -8
- package/dist/server/theme.mjs +27 -8
- package/dist/styles/index.css +15 -0
- package/package.json +1 -1
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +0 -48
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +0 -17
- package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +0 -100
- /package/dist/_dts/blocks/src/system/runtime/nodes/events/{shared → domain}/destination.d.ts +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { a as buildBookingPaymentSelectionViewModel, i as usePolling, n as StepIndicator$1, o as resolveBookingPaymentDueNowCents, r as SpinnerNode, s as formatPrice, t as BookingConsentAgreement } from "./BookingConsentAgreement
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
2
|
+
import { a as buildBookingPaymentSelectionViewModel, i as usePolling, n as StepIndicator$1, o as resolveBookingPaymentDueNowCents, r as SpinnerNode, s as formatPrice, t as BookingConsentAgreement } from "./BookingConsentAgreement--_fUCZe-.mjs";
|
|
3
|
+
import { At as formatBookingDateTime, B as formatEventPriceDisplay, Bt as toScheduledBookingDateTime, C as getStepNumber, Gt as publicEventTicketTypes, Ht as publicEventPricingMode, Kt as formatCurrencyAmount, Mt as formatBookingTime, O as resolveEventRegistrationScopedEvents, Ot as formatBookingDate, Rt as toBookingTimeZone, S as WIZARD_STEPS, T as planEventRegistrationSurface, Ut as publicEventPricingType, V as resolveRuntimeEventPricing, Vt as publicEventPricingCurrency, Wt as publicEventScalarPrice, X as themeButtonClassName, _ as findEventByOccurrenceId, b as resolveEventRequiresPayment, g as toWholeSeriesRegistrationEvents, h as toRegistrationEvents, t as defineIslandPropsDecoder, v as findPreSelectedEvent, w as getWizardStepsForContext, x as resolveRegistrationTarget, y as resolveDiscountContext, yt as assertNever } from "./propsDecoder-pYvx_Srl.mjs";
|
|
4
|
+
import { a as formatTicketTypeRemaining, d as selectEventCapacitySource, f as selectMostOpenCapacitySource, i as formatRemainingPlaces, l as getRemainingPlaces, n as canJoinEventWaitlistOnAnyMode, r as formatEventRemainingPlaces, s as getEventAvailability, t as canJoinEventWaitlist, u as isEventFull } from "./capacity-BW00PO7s.mjs";
|
|
5
|
+
import { n as useBlockApi, r as BlockApiError, t as BlockApiProvider } from "./context-DCkiHVdD.mjs";
|
|
5
6
|
import { n as isSpamProtectionEnabled, r as AltchaWidget, t as getChallengeUrl } from "./src-B833rYRr.mjs";
|
|
6
|
-
import { a as CheckIcon, i as StateIcon, n as useDiscountCodeUi, r as TerminalPanel, t as scrollToBlock } from "./scrollToBlock-
|
|
7
|
-
import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-
|
|
7
|
+
import { a as CheckIcon, i as StateIcon, n as useDiscountCodeUi, r as TerminalPanel, t as scrollToBlock } from "./scrollToBlock-ByQv96M2.mjs";
|
|
8
|
+
import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-BMipSoDJ.mjs";
|
|
8
9
|
import { t as FeedbackMessage } from "./FeedbackMessage-CtLvvS_d.mjs";
|
|
9
10
|
import { n as getFieldValidationControlProps, t as FieldValidationMessage } from "./FieldValidationMessage-AYanWFb-.mjs";
|
|
10
11
|
import { t as buildCheckoutReturnUrls } from "./checkoutUrls-BuseL0dw.mjs";
|
|
@@ -713,10 +714,12 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
|
|
|
713
714
|
className: "er-event-grid",
|
|
714
715
|
children: events.map((event) => {
|
|
715
716
|
const isSelected = selectedEvent?.selectionId === event.selectionId;
|
|
716
|
-
const
|
|
717
|
-
const
|
|
717
|
+
const remaining = getRemainingPlaces(selectMostOpenCapacitySource(event));
|
|
718
|
+
const isSoldOut = isEventFull(event);
|
|
719
|
+
const canJoinWaitlist = canJoinEventWaitlistOnAnyMode(event);
|
|
718
720
|
const isUnavailable = isSoldOut && !canJoinWaitlist;
|
|
719
|
-
const isLowCapacity =
|
|
721
|
+
const isLowCapacity = remaining.kind === "low";
|
|
722
|
+
const capacityLabel = formatEventRemainingPlaces(event, { full: "Sold out" });
|
|
720
723
|
const timeZone = event.timeZone ?? null;
|
|
721
724
|
const cardClasses = [
|
|
722
725
|
"er-event-card",
|
|
@@ -748,9 +751,9 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
|
|
|
748
751
|
children: " · "
|
|
749
752
|
}), event.venue.name] })]
|
|
750
753
|
})]
|
|
751
|
-
}), showCapacity &&
|
|
754
|
+
}), showCapacity && capacityLabel && /* @__PURE__ */ jsx("span", {
|
|
752
755
|
className: badgeClasses,
|
|
753
|
-
children:
|
|
756
|
+
children: capacityLabel
|
|
754
757
|
})]
|
|
755
758
|
})
|
|
756
759
|
}, event.selectionId);
|
|
@@ -759,6 +762,61 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
|
|
|
759
762
|
});
|
|
760
763
|
};
|
|
761
764
|
//#endregion
|
|
765
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
|
|
766
|
+
function resolveRegistrationRecoveryAction(step, action) {
|
|
767
|
+
if (step === "payment-failed") return "payment-options";
|
|
768
|
+
if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "payment-status";
|
|
769
|
+
return action;
|
|
770
|
+
}
|
|
771
|
+
function canRecoverToWaitlist(eligibility) {
|
|
772
|
+
return Boolean(eligibility?.isFull && eligibility.canJoinWaitlist);
|
|
773
|
+
}
|
|
774
|
+
function shouldRefreshAfterRegistrationError(step, action, enabled) {
|
|
775
|
+
return enabled || step === "error" && action === "places";
|
|
776
|
+
}
|
|
777
|
+
function resetRegistrationAfterExpiry(input) {
|
|
778
|
+
input.dispatchFlow({
|
|
779
|
+
type: "reset",
|
|
780
|
+
hasPreSelectedContext: input.hasPreSelectedContext
|
|
781
|
+
});
|
|
782
|
+
input.setSelectedTicketLines([]);
|
|
783
|
+
input.setSelectedSlidingAmountInput("");
|
|
784
|
+
const mode = input.preSelectedEvent?.deliveryMode;
|
|
785
|
+
input.setSelectedAttendanceMode(mode === "online" || mode === "in_person" ? mode : null);
|
|
786
|
+
input.setErrorMessage("");
|
|
787
|
+
input.setPendingRegistrationReference(null);
|
|
788
|
+
}
|
|
789
|
+
/** Recovery never starts another payment. Unknown payment outcomes go to the portal. */
|
|
790
|
+
function recoverRegistrationError(action, handlers) {
|
|
791
|
+
switch (action) {
|
|
792
|
+
case "none": return;
|
|
793
|
+
case "portal":
|
|
794
|
+
case "payment-status":
|
|
795
|
+
window.location.assign("/portal");
|
|
796
|
+
return;
|
|
797
|
+
case "event":
|
|
798
|
+
window.location.assign(window.location.pathname);
|
|
799
|
+
return;
|
|
800
|
+
case "retry":
|
|
801
|
+
window.location.reload();
|
|
802
|
+
return;
|
|
803
|
+
case "details":
|
|
804
|
+
handlers.clearError();
|
|
805
|
+
handlers.setStep("enter-details");
|
|
806
|
+
return;
|
|
807
|
+
case "payment-options":
|
|
808
|
+
case "places":
|
|
809
|
+
handlers.refreshOptions();
|
|
810
|
+
handlers.clearError();
|
|
811
|
+
handlers.setStep(action === "places" ? handlers.canJoinWaitlist ? "enter-details" : "select-tickets" : "select-payment");
|
|
812
|
+
return;
|
|
813
|
+
case "start":
|
|
814
|
+
handlers.refreshOptions();
|
|
815
|
+
handlers.reset();
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
//#endregion
|
|
762
820
|
//#region ../blocks/src/system/runtime/nodes/event-registration/validation.ts
|
|
763
821
|
function createInitialRegistrationFormData() {
|
|
764
822
|
return {
|
|
@@ -1002,11 +1060,11 @@ function useEligibilityWithDeps(params, deps) {
|
|
|
1002
1060
|
type: "resolved",
|
|
1003
1061
|
eligibility: transformRegistrationOptionsResponse(result)
|
|
1004
1062
|
});
|
|
1005
|
-
} catch
|
|
1063
|
+
} catch {
|
|
1006
1064
|
if (cancelled || requestIdRef.current !== requestId) return;
|
|
1007
1065
|
dispatch({
|
|
1008
1066
|
type: "failed",
|
|
1009
|
-
message:
|
|
1067
|
+
message: "We couldn’t load the booking options for this event. Please refresh this page and try again. If this keeps happening, contact the organiser for help."
|
|
1010
1068
|
});
|
|
1011
1069
|
}
|
|
1012
1070
|
}
|
|
@@ -1018,6 +1076,7 @@ function useEligibilityWithDeps(params, deps) {
|
|
|
1018
1076
|
}, [
|
|
1019
1077
|
api,
|
|
1020
1078
|
params.enabled,
|
|
1079
|
+
params.refreshKey,
|
|
1021
1080
|
params.subject?.kind,
|
|
1022
1081
|
params.subject?.kind === "occurrence" ? params.subject.occurrenceId : params.subject?.eventSeriesId,
|
|
1023
1082
|
params.attendanceMode,
|
|
@@ -1127,8 +1186,252 @@ function logoutPortal(anchorId) {
|
|
|
1127
1186
|
form.submit();
|
|
1128
1187
|
}
|
|
1129
1188
|
//#endregion
|
|
1189
|
+
//#region ../commerce/src/purchase/checkoutPaymentFlow.ts
|
|
1190
|
+
function planCheckoutPaymentFlow(options) {
|
|
1191
|
+
const amountDueCents = toNonNegativeCents(options.amountDueCents);
|
|
1192
|
+
const discountCents = toNonNegativeCents(options.discountCents);
|
|
1193
|
+
const availableCreditCents = toNonNegativeCents(options.availableCreditCents);
|
|
1194
|
+
const minimumStripeChargeCents = toNonNegativeCents(options.minimumStripeChargeCents ?? 0);
|
|
1195
|
+
if (amountDueCents <= 0) return {
|
|
1196
|
+
kind: "bypass",
|
|
1197
|
+
bypassReason: discountCents > 0 ? "discount_free" : "free",
|
|
1198
|
+
reserveCreditCents: 0
|
|
1199
|
+
};
|
|
1200
|
+
if (!options.creditCurrencyMatches || availableCreditCents <= 0) return {
|
|
1201
|
+
kind: "stripe",
|
|
1202
|
+
reserveCreditCents: toNonNegativeCents(0)
|
|
1203
|
+
};
|
|
1204
|
+
if (availableCreditCents >= amountDueCents) return {
|
|
1205
|
+
kind: "store_credit_only",
|
|
1206
|
+
reserveCreditCents: amountDueCents
|
|
1207
|
+
};
|
|
1208
|
+
return {
|
|
1209
|
+
kind: "stripe",
|
|
1210
|
+
reserveCreditCents: planMixedPaymentCreditReservation({
|
|
1211
|
+
amountDueCents,
|
|
1212
|
+
availableCreditCents,
|
|
1213
|
+
minimumStripeChargeCents
|
|
1214
|
+
})
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
function planMixedPaymentCreditReservation(input) {
|
|
1218
|
+
const chargeAfterFullCredit = input.amountDueCents - input.availableCreditCents;
|
|
1219
|
+
if (input.minimumStripeChargeCents <= 0 || chargeAfterFullCredit <= 0 || chargeAfterFullCredit >= input.minimumStripeChargeCents) return input.availableCreditCents;
|
|
1220
|
+
if (input.amountDueCents < input.minimumStripeChargeCents) return toNonNegativeCents(0);
|
|
1221
|
+
return toNonNegativeCents(input.amountDueCents - input.minimumStripeChargeCents);
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Apply site credit to a flexible-balance booking before card payment.
|
|
1225
|
+
*
|
|
1226
|
+
* Credit first reduces the configured payment due now. Once that payment is
|
|
1227
|
+
* covered, any remaining credit reduces the later balance. If a partial credit
|
|
1228
|
+
* application would leave a positive card charge below Stripe's minimum, less
|
|
1229
|
+
* credit is used so the configured payment can still be collected.
|
|
1230
|
+
*/
|
|
1231
|
+
function planFlexibleBalanceCreditFirst(input) {
|
|
1232
|
+
const totalCents = toNonNegativeCents(input.totalAfterDiscountCents);
|
|
1233
|
+
const configuredDueNowCents = toNonNegativeCents(Math.min(input.configuredPaymentDueNowCents, totalCents));
|
|
1234
|
+
const availableCreditCents = toNonNegativeCents(input.creditCurrencyMatches ? input.availableCreditCents : 0);
|
|
1235
|
+
const minimumStripeChargeCents = toNonNegativeCents(input.minimumStripeChargeCents ?? 0);
|
|
1236
|
+
if (availableCreditCents >= totalCents && totalCents > 0) return {
|
|
1237
|
+
kind: "credit_only",
|
|
1238
|
+
reserveCreditCents: totalCents,
|
|
1239
|
+
cardDueNowCents: toNonNegativeCents(0),
|
|
1240
|
+
laterBalanceCents: toNonNegativeCents(0)
|
|
1241
|
+
};
|
|
1242
|
+
let reserveCreditCents = toNonNegativeCents(Math.min(availableCreditCents, totalCents));
|
|
1243
|
+
if (reserveCreditCents >= configuredDueNowCents) return {
|
|
1244
|
+
kind: "flexible_balance",
|
|
1245
|
+
reserveCreditCents,
|
|
1246
|
+
cardDueNowCents: toNonNegativeCents(0),
|
|
1247
|
+
laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents)
|
|
1248
|
+
};
|
|
1249
|
+
const plannedCardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
|
|
1250
|
+
if (plannedCardDueNowCents > 0 && plannedCardDueNowCents < minimumStripeChargeCents) {
|
|
1251
|
+
if (configuredDueNowCents < minimumStripeChargeCents) return {
|
|
1252
|
+
kind: "card_below_minimum",
|
|
1253
|
+
reserveCreditCents: toNonNegativeCents(0),
|
|
1254
|
+
cardDueNowCents: configuredDueNowCents,
|
|
1255
|
+
laterBalanceCents: toNonNegativeCents(totalCents - configuredDueNowCents)
|
|
1256
|
+
};
|
|
1257
|
+
reserveCreditCents = toNonNegativeCents(configuredDueNowCents - minimumStripeChargeCents);
|
|
1258
|
+
}
|
|
1259
|
+
const cardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
|
|
1260
|
+
return {
|
|
1261
|
+
kind: "flexible_balance",
|
|
1262
|
+
reserveCreditCents,
|
|
1263
|
+
cardDueNowCents,
|
|
1264
|
+
laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents - cardDueNowCents)
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
//#endregion
|
|
1268
|
+
//#region ../commerce/src/purchase/settlement.ts
|
|
1269
|
+
function normalizeCurrencyCode(raw) {
|
|
1270
|
+
const normalized = raw.trim().toUpperCase();
|
|
1271
|
+
if (!/^[A-Z]{3}$/.test(normalized)) throw new Error(`Invalid currency code: ${raw}`);
|
|
1272
|
+
return normalized;
|
|
1273
|
+
}
|
|
1274
|
+
//#endregion
|
|
1275
|
+
//#region ../commerce/src/purchase/checkoutSessionMetadata.ts
|
|
1276
|
+
const datedOfferingSharedEngineMetadataShape = {
|
|
1277
|
+
datedOfferingEngineContractVersion: z.literal("shared_v1"),
|
|
1278
|
+
datedOfferingEngineRouteKind: z.literal("shared_whole_series_registration"),
|
|
1279
|
+
datedOfferingCapacityHoldId: z.string().uuid(),
|
|
1280
|
+
datedOfferingCapacityHoldAuditRef: z.string().uuid(),
|
|
1281
|
+
customerId: z.string().uuid(),
|
|
1282
|
+
eventSeriesId: z.string().uuid()
|
|
1283
|
+
};
|
|
1284
|
+
z.object(datedOfferingSharedEngineMetadataShape);
|
|
1285
|
+
const orderCheckoutSessionMetadataBaseShape = {
|
|
1286
|
+
type: z.literal("order"),
|
|
1287
|
+
site_id: z.string().uuid(),
|
|
1288
|
+
order_id: z.string().uuid().optional(),
|
|
1289
|
+
customer_email: z.string().email(),
|
|
1290
|
+
customer_name: z.string().optional(),
|
|
1291
|
+
product_id: z.string().uuid().optional(),
|
|
1292
|
+
attendee_id: z.string().uuid().optional(),
|
|
1293
|
+
occurrence_id: z.string().uuid().optional(),
|
|
1294
|
+
tickets_count: z.string().optional(),
|
|
1295
|
+
notes: z.string().optional(),
|
|
1296
|
+
discount_redemption_id: z.string().uuid().optional(),
|
|
1297
|
+
discount_code_id: z.string().uuid().optional(),
|
|
1298
|
+
discount_code: z.string().optional(),
|
|
1299
|
+
discount_public_label: z.string().optional(),
|
|
1300
|
+
credit_reservation_id: z.string().uuid().optional(),
|
|
1301
|
+
credit_reserved_cents: z.string().optional(),
|
|
1302
|
+
discount_code_amount_cents: z.string().optional(),
|
|
1303
|
+
order_payment_reason: z.enum([
|
|
1304
|
+
"instalment_payoff",
|
|
1305
|
+
"flexible_balance_initial",
|
|
1306
|
+
"flexible_balance_paydown"
|
|
1307
|
+
]).optional(),
|
|
1308
|
+
order_payment_plan_id: z.string().uuid().optional(),
|
|
1309
|
+
order_flexible_balance_contract_id: z.string().uuid().optional()
|
|
1310
|
+
};
|
|
1311
|
+
const absentDatedOfferingSharedEngineMetadataShape = {
|
|
1312
|
+
datedOfferingEngineContractVersion: z.never().optional(),
|
|
1313
|
+
datedOfferingEngineRouteKind: z.never().optional(),
|
|
1314
|
+
datedOfferingCapacityHoldId: z.never().optional(),
|
|
1315
|
+
datedOfferingCapacityHoldAuditRef: z.never().optional(),
|
|
1316
|
+
customerId: z.never().optional(),
|
|
1317
|
+
eventSeriesId: z.never().optional()
|
|
1318
|
+
};
|
|
1319
|
+
const LegacyOrderCheckoutSessionMetadataSchema = z.object({
|
|
1320
|
+
...orderCheckoutSessionMetadataBaseShape,
|
|
1321
|
+
...absentDatedOfferingSharedEngineMetadataShape
|
|
1322
|
+
});
|
|
1323
|
+
const SharedDatedOfferingOrderCheckoutSessionMetadataSchema = z.object({
|
|
1324
|
+
...orderCheckoutSessionMetadataBaseShape,
|
|
1325
|
+
...datedOfferingSharedEngineMetadataShape
|
|
1326
|
+
});
|
|
1327
|
+
z.union([SharedDatedOfferingOrderCheckoutSessionMetadataSchema, LegacyOrderCheckoutSessionMetadataSchema]);
|
|
1328
|
+
toNonNegativeCents(0);
|
|
1329
|
+
//#endregion
|
|
1330
|
+
//#region ../commerce/src/purchase/stripeMinimumCharge.ts
|
|
1331
|
+
const STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY = {
|
|
1332
|
+
GBP: 30,
|
|
1333
|
+
USD: 50,
|
|
1334
|
+
EUR: 50,
|
|
1335
|
+
AED: 200,
|
|
1336
|
+
AUD: 50,
|
|
1337
|
+
BGN: 100,
|
|
1338
|
+
BRL: 50,
|
|
1339
|
+
CAD: 50,
|
|
1340
|
+
CHF: 50,
|
|
1341
|
+
CZK: 1500,
|
|
1342
|
+
DKK: 250,
|
|
1343
|
+
HKD: 400,
|
|
1344
|
+
HUF: 17500,
|
|
1345
|
+
JPY: 50,
|
|
1346
|
+
MXN: 1e3,
|
|
1347
|
+
MYR: 200,
|
|
1348
|
+
NOK: 300,
|
|
1349
|
+
NZD: 50,
|
|
1350
|
+
PLN: 200,
|
|
1351
|
+
RON: 200,
|
|
1352
|
+
SEK: 300,
|
|
1353
|
+
SGD: 50,
|
|
1354
|
+
THB: 1e3
|
|
1355
|
+
};
|
|
1356
|
+
function isStripeMinimumChargeCurrency(currency) {
|
|
1357
|
+
return currency in STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY;
|
|
1358
|
+
}
|
|
1359
|
+
function getStripeMinimumChargeRequirement(rawCurrency) {
|
|
1360
|
+
let currency;
|
|
1361
|
+
try {
|
|
1362
|
+
currency = normalizeCurrencyCode(rawCurrency);
|
|
1363
|
+
} catch {
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
const currencyKey = currency;
|
|
1367
|
+
if (!isStripeMinimumChargeCurrency(currencyKey)) return null;
|
|
1368
|
+
return {
|
|
1369
|
+
currency,
|
|
1370
|
+
minimumAmountCents: STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY[currencyKey]
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
//#endregion
|
|
1374
|
+
//#region ../commerce/src/purchase/event-registration-errors.ts
|
|
1375
|
+
const eventRegistrationErrors = {
|
|
1376
|
+
registrationClosed: {
|
|
1377
|
+
message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
|
|
1378
|
+
action: "none"
|
|
1379
|
+
},
|
|
1380
|
+
paymentUncertain: {
|
|
1381
|
+
message: "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.",
|
|
1382
|
+
action: "payment-status"
|
|
1383
|
+
},
|
|
1384
|
+
paymentPageUnavailable: {
|
|
1385
|
+
message: "We couldn’t open the payment page. Please refresh this page and try again. If this keeps happening, contact the organiser for help.",
|
|
1386
|
+
action: "retry"
|
|
1387
|
+
},
|
|
1388
|
+
paymentUnavailable: {
|
|
1389
|
+
message: "Online payment is not available for this event right now. Please contact the organiser for help with booking.",
|
|
1390
|
+
action: "none"
|
|
1391
|
+
},
|
|
1392
|
+
capacityChanged: {
|
|
1393
|
+
message: "Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.",
|
|
1394
|
+
action: "places"
|
|
1395
|
+
},
|
|
1396
|
+
selectionChanged: {
|
|
1397
|
+
message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
|
|
1398
|
+
action: "places"
|
|
1399
|
+
},
|
|
1400
|
+
registrationPreparationChanged: {
|
|
1401
|
+
message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
|
|
1402
|
+
action: "places"
|
|
1403
|
+
},
|
|
1404
|
+
registrationAlreadyExists: {
|
|
1405
|
+
message: "You already have a booking for this event. Please check your bookings to see its status. If you need help, contact the organiser.",
|
|
1406
|
+
action: "portal"
|
|
1407
|
+
},
|
|
1408
|
+
holdExpired: {
|
|
1409
|
+
message: "Your reservation has timed out. Please check availability and choose your places again.",
|
|
1410
|
+
action: "start"
|
|
1411
|
+
},
|
|
1412
|
+
waitlistUnavailable: {
|
|
1413
|
+
message: "Sorry, the waiting list place offered in this invitation is no longer available. Please check the event for available places or contact the organiser for help.",
|
|
1414
|
+
action: "event"
|
|
1415
|
+
},
|
|
1416
|
+
paymentContextMismatch: {
|
|
1417
|
+
message: "The payment options for this event have changed. Please check the latest options before continuing.",
|
|
1418
|
+
action: "payment-options"
|
|
1419
|
+
},
|
|
1420
|
+
idempotencyConflict: {
|
|
1421
|
+
message: "You’ve already started booking this event. Please finish in the other tab if it’s still open, or check your booking status before starting again.",
|
|
1422
|
+
action: "portal"
|
|
1423
|
+
},
|
|
1424
|
+
invalidCommitInput: {
|
|
1425
|
+
message: "We couldn’t accept your booking details. Please check the information you entered and try again. If this keeps happening, contact the organiser for help.",
|
|
1426
|
+
action: "details"
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
function parseEventRegistrationErrorReason(value) {
|
|
1430
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(eventRegistrationErrors, value) ? value : null;
|
|
1431
|
+
}
|
|
1432
|
+
//#endregion
|
|
1130
1433
|
//#region ../blocks/src/system/runtime/shared/useWaitlistClaim.state.ts
|
|
1131
|
-
const DEFAULT_WAITLIST_CLAIM_ERROR = "
|
|
1434
|
+
const DEFAULT_WAITLIST_CLAIM_ERROR = "Online booking is unavailable right now. Please contact the organiser for help with booking.";
|
|
1132
1435
|
function createInitialWaitlistClaimState(hasToken) {
|
|
1133
1436
|
return hasToken ? { kind: "resolving" } : { kind: "idle" };
|
|
1134
1437
|
}
|
|
@@ -1144,14 +1447,13 @@ function waitlistClaimReducer(_state, action) {
|
|
|
1144
1447
|
}
|
|
1145
1448
|
}
|
|
1146
1449
|
function toWaitlistClaimResolveFailure(error) {
|
|
1147
|
-
const message = error instanceof Error ? error.message : DEFAULT_WAITLIST_CLAIM_ERROR;
|
|
1148
1450
|
if (isUnauthorizedBlockApiError(error)) return {
|
|
1149
1451
|
kind: "portal-login-required",
|
|
1150
|
-
message
|
|
1452
|
+
message: "Please log in to continue with your waiting list invitation."
|
|
1151
1453
|
};
|
|
1152
1454
|
return {
|
|
1153
1455
|
kind: "error",
|
|
1154
|
-
message
|
|
1456
|
+
message: hasStatus(error) && (error.status === 404 || error.status === 409 || error.status === 410) ? eventRegistrationErrors.waitlistUnavailable.message : DEFAULT_WAITLIST_CLAIM_ERROR
|
|
1155
1457
|
};
|
|
1156
1458
|
}
|
|
1157
1459
|
function isUnauthorizedBlockApiError(error) {
|
|
@@ -1512,11 +1814,21 @@ function cleanupStaleRegistrations() {
|
|
|
1512
1814
|
//#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.ts
|
|
1513
1815
|
const eventRegistrationStripeReturnDeps = {
|
|
1514
1816
|
usePaymentVerification,
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1817
|
+
readReturnParams: () => {
|
|
1818
|
+
const params = new URLSearchParams(window.location.search);
|
|
1819
|
+
return {
|
|
1820
|
+
registration: params.get("registration"),
|
|
1821
|
+
registration_attempt: params.get(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM),
|
|
1822
|
+
session_id: params.get("session_id")
|
|
1823
|
+
};
|
|
1824
|
+
},
|
|
1825
|
+
clearReturnParams: () => {
|
|
1826
|
+
consumeSearchParams([
|
|
1827
|
+
"registration",
|
|
1828
|
+
EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM,
|
|
1829
|
+
"session_id"
|
|
1830
|
+
]);
|
|
1831
|
+
},
|
|
1520
1832
|
scrollToBlock,
|
|
1521
1833
|
cleanupStaleRegistrations,
|
|
1522
1834
|
clearPendingRegistration,
|
|
@@ -1537,52 +1849,66 @@ function showReturnReconciliation(options, message) {
|
|
|
1537
1849
|
options.setErrorMessage(message);
|
|
1538
1850
|
options.setStep("verifying-timeout");
|
|
1539
1851
|
}
|
|
1852
|
+
function checkoutReturnCommand(params) {
|
|
1853
|
+
const checkoutAttemptId = params.registration_attempt;
|
|
1854
|
+
if (!checkoutAttemptId) return null;
|
|
1855
|
+
if (params.registration === "cancelled") return {
|
|
1856
|
+
kind: "abandon",
|
|
1857
|
+
checkoutAttemptId
|
|
1858
|
+
};
|
|
1859
|
+
if (params.registration === "success" && params.session_id) return {
|
|
1860
|
+
kind: "resolveSuccess",
|
|
1861
|
+
checkoutAttemptId,
|
|
1862
|
+
sessionId: params.session_id
|
|
1863
|
+
};
|
|
1864
|
+
return null;
|
|
1865
|
+
}
|
|
1540
1866
|
async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
|
|
1541
1867
|
deps.cleanupStaleRegistrations();
|
|
1542
|
-
const params = deps.
|
|
1543
|
-
if (params.registration !== "cancelled" && params.registration !== "success") return;
|
|
1868
|
+
const params = deps.readReturnParams();
|
|
1869
|
+
if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
|
|
1544
1870
|
deps.scrollToBlock(EVENT_REGISTRATION_ANCHOR_ID);
|
|
1545
1871
|
const siteId = options.api.config.siteId;
|
|
1546
|
-
const
|
|
1547
|
-
if (!siteId) return;
|
|
1548
|
-
if (!
|
|
1549
|
-
showReturnReconciliation(options, "We
|
|
1550
|
-
return;
|
|
1872
|
+
const command = checkoutReturnCommand(params);
|
|
1873
|
+
if (!siteId) return "settled";
|
|
1874
|
+
if (!command) {
|
|
1875
|
+
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.");
|
|
1876
|
+
return "settled";
|
|
1551
1877
|
}
|
|
1878
|
+
const { checkoutAttemptId } = command;
|
|
1552
1879
|
const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
|
|
1553
1880
|
checkoutAttemptIdRef.current = checkoutAttemptId;
|
|
1554
1881
|
if (pending) restorePendingPresentation(options, pending);
|
|
1882
|
+
options.setStep("verifying-payment");
|
|
1555
1883
|
try {
|
|
1556
|
-
const outcome =
|
|
1557
|
-
kind: "resolveSuccess",
|
|
1558
|
-
checkoutAttemptId,
|
|
1559
|
-
sessionId: params.session_id
|
|
1560
|
-
}) : { kind: "notFound" } : await options.api.resolveEventCheckoutReturn({
|
|
1561
|
-
kind: "abandon",
|
|
1562
|
-
checkoutAttemptId
|
|
1563
|
-
});
|
|
1884
|
+
const outcome = await options.api.resolveEventCheckoutReturn(command);
|
|
1564
1885
|
switch (outcome.kind) {
|
|
1565
1886
|
case "resolved":
|
|
1887
|
+
deps.clearReturnParams();
|
|
1566
1888
|
options.setPendingRegistrationReference(outcome.reference);
|
|
1567
1889
|
options.setStep("verifying-payment");
|
|
1568
|
-
return;
|
|
1890
|
+
return "settled";
|
|
1569
1891
|
case "abandoned":
|
|
1892
|
+
deps.clearReturnParams();
|
|
1570
1893
|
options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
|
|
1571
1894
|
options.setStep("select-payment");
|
|
1572
|
-
return;
|
|
1895
|
+
return "settled";
|
|
1573
1896
|
case "notFound":
|
|
1574
1897
|
if (params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
|
|
1898
|
+
deps.clearReturnParams();
|
|
1575
1899
|
options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
|
|
1576
1900
|
options.setStep("select-payment");
|
|
1577
|
-
return;
|
|
1901
|
+
return "settled";
|
|
1578
1902
|
}
|
|
1579
|
-
|
|
1903
|
+
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.");
|
|
1904
|
+
return "settled";
|
|
1580
1905
|
case "reconciliationRequired": break;
|
|
1581
1906
|
}
|
|
1582
|
-
showReturnReconciliation(options, "We’re checking your payment. Please
|
|
1907
|
+
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.");
|
|
1583
1908
|
} catch {
|
|
1584
|
-
showReturnReconciliation(options, "We
|
|
1909
|
+
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.");
|
|
1585
1910
|
}
|
|
1911
|
+
return "retryable";
|
|
1586
1912
|
}
|
|
1587
1913
|
function useEventRegistrationStripeReturn(options) {
|
|
1588
1914
|
return useEventRegistrationStripeReturnWithDeps(options, eventRegistrationStripeReturnDeps);
|
|
@@ -1590,10 +1916,34 @@ function useEventRegistrationStripeReturn(options) {
|
|
|
1590
1916
|
function useEventRegistrationStripeReturnWithDeps(options, deps) {
|
|
1591
1917
|
const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
|
|
1592
1918
|
const checkoutAttemptIdRef = useRef(null);
|
|
1919
|
+
const lookupInFlightRef = useRef(false);
|
|
1920
|
+
const [lookupState, setLookupState] = useState("settled");
|
|
1921
|
+
const resolveReturn = useCallback(async () => {
|
|
1922
|
+
if (lookupInFlightRef.current) return;
|
|
1923
|
+
lookupInFlightRef.current = true;
|
|
1924
|
+
setLookupState("loading");
|
|
1925
|
+
try {
|
|
1926
|
+
setLookupState(await handleCheckoutReturn(options, deps, checkoutAttemptIdRef));
|
|
1927
|
+
} finally {
|
|
1928
|
+
lookupInFlightRef.current = false;
|
|
1929
|
+
}
|
|
1930
|
+
}, [options, deps]);
|
|
1593
1931
|
const verificationConfig = useMemo(() => step === "verifying-payment" && pendingRegistrationReference ? { reference: pendingRegistrationReference } : null, [step, pendingRegistrationReference]);
|
|
1594
1932
|
const verification = deps.usePaymentVerification(verificationConfig);
|
|
1933
|
+
const hasVerificationTimeout = step === "verifying-timeout" && pendingRegistrationReference !== null;
|
|
1934
|
+
const retry = useCallback(() => {
|
|
1935
|
+
if (hasVerificationTimeout) {
|
|
1936
|
+
setStep("verifying-payment");
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
verification.retry();
|
|
1940
|
+
}, [
|
|
1941
|
+
hasVerificationTimeout,
|
|
1942
|
+
setStep,
|
|
1943
|
+
verification.retry
|
|
1944
|
+
]);
|
|
1595
1945
|
useEffect(() => {
|
|
1596
|
-
|
|
1946
|
+
resolveReturn();
|
|
1597
1947
|
}, []);
|
|
1598
1948
|
useEffect(() => {
|
|
1599
1949
|
if (verification.status === "completed") {
|
|
@@ -1603,7 +1953,7 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
|
|
|
1603
1953
|
setRegistrationResult({ status: "confirmed" });
|
|
1604
1954
|
setStep("success");
|
|
1605
1955
|
} else if (verification.status === "failed") {
|
|
1606
|
-
setErrorMessage(
|
|
1956
|
+
setErrorMessage("Please try again or choose another payment option. If this keeps happening, contact the organiser for help.");
|
|
1607
1957
|
setStep("payment-failed");
|
|
1608
1958
|
} else if (verification.status === "timeout") setStep("verifying-timeout");
|
|
1609
1959
|
}, [
|
|
@@ -1616,7 +1966,22 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
|
|
|
1616
1966
|
setRegistrationResult,
|
|
1617
1967
|
setStep
|
|
1618
1968
|
]);
|
|
1619
|
-
return
|
|
1969
|
+
return {
|
|
1970
|
+
...verification,
|
|
1971
|
+
status: hasVerificationTimeout ? "timeout" : verification.status,
|
|
1972
|
+
retry,
|
|
1973
|
+
retryReturn: lookupState === "retryable" ? () => {
|
|
1974
|
+
resolveReturn();
|
|
1975
|
+
} : void 0
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
//#endregion
|
|
1979
|
+
//#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.ts
|
|
1980
|
+
function registrationErrorPresentation(error) {
|
|
1981
|
+
const reason = error instanceof BlockApiError ? parseEventRegistrationErrorReason(error.tag) : null;
|
|
1982
|
+
if (reason) return eventRegistrationErrors[reason];
|
|
1983
|
+
if (error instanceof BlockApiError && error.status < 500) return eventRegistrationErrors.invalidCommitInput;
|
|
1984
|
+
return eventRegistrationErrors.paymentUncertain;
|
|
1620
1985
|
}
|
|
1621
1986
|
//#endregion
|
|
1622
1987
|
//#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.ts
|
|
@@ -1705,7 +2070,8 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
|
|
|
1705
2070
|
outcome: { kind: "completed" }
|
|
1706
2071
|
};
|
|
1707
2072
|
} catch (error) {
|
|
1708
|
-
const message
|
|
2073
|
+
const { message, action } = registrationErrorPresentation(error);
|
|
2074
|
+
options.setErrorAction?.(action);
|
|
1709
2075
|
options.setErrorMessage(message);
|
|
1710
2076
|
options.setStep("error");
|
|
1711
2077
|
return {
|
|
@@ -1870,6 +2236,13 @@ function redirectToCheckout(checkoutUrl, submitOptions) {
|
|
|
1870
2236
|
}
|
|
1871
2237
|
//#endregion
|
|
1872
2238
|
//#region ../blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.ts
|
|
2239
|
+
function getEventSelectionDefaults(event) {
|
|
2240
|
+
const suggestedAmount = getSlidingScaleForEvent(event)?.suggestedAmount;
|
|
2241
|
+
return {
|
|
2242
|
+
slidingAmountInput: suggestedAmount == null ? "" : formatCentsAsCurrencyInput(suggestedAmount),
|
|
2243
|
+
attendanceMode: event.deliveryMode === "hybrid" ? null : event.deliveryMode ?? "in_person"
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
1873
2246
|
function parseCurrencyInputToCents(value) {
|
|
1874
2247
|
const trimmed = value.trim();
|
|
1875
2248
|
if (!trimmed) return null;
|
|
@@ -1917,7 +2290,7 @@ function resolveWaitlistClaimRestoration(input) {
|
|
|
1917
2290
|
const event = findEventByOccurrenceId(input.scopedEvents, subject.occurrenceId);
|
|
1918
2291
|
if (!event && input.occurrenceContextId !== subject.occurrenceId) return {
|
|
1919
2292
|
ok: false,
|
|
1920
|
-
message: "This
|
|
2293
|
+
message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
|
|
1921
2294
|
};
|
|
1922
2295
|
return {
|
|
1923
2296
|
ok: true,
|
|
@@ -1934,7 +2307,7 @@ function resolveWaitlistClaimRestoration(input) {
|
|
|
1934
2307
|
const event = input.scopedEvents.find((candidate) => candidate.registrationSubject.kind === "wholeSeries" && candidate.registrationSubject.eventSeriesId === subject.eventSeriesId);
|
|
1935
2308
|
if (!event) return {
|
|
1936
2309
|
ok: false,
|
|
1937
|
-
message: "This
|
|
2310
|
+
message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
|
|
1938
2311
|
};
|
|
1939
2312
|
return {
|
|
1940
2313
|
ok: true,
|
|
@@ -1967,6 +2340,7 @@ function useWaitlistClaimRestoration(input) {
|
|
|
1967
2340
|
occurrenceContextId: input.occurrenceContextId
|
|
1968
2341
|
});
|
|
1969
2342
|
if (!restoration.ok) {
|
|
2343
|
+
input.setters.setErrorAction?.("none");
|
|
1970
2344
|
input.setters.setErrorMessage(restoration.message);
|
|
1971
2345
|
input.setters.setStep("error");
|
|
1972
2346
|
return;
|
|
@@ -2045,6 +2419,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2045
2419
|
});
|
|
2046
2420
|
const [registrationResult, setRegistrationResult] = useState(null);
|
|
2047
2421
|
const [errorMessage, setErrorMessage] = useState("");
|
|
2422
|
+
const [errorAction, setErrorAction] = useState("retry");
|
|
2423
|
+
const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
|
|
2048
2424
|
const [altchaSolution, setAltchaSolution] = useState(null);
|
|
2049
2425
|
const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
|
|
2050
2426
|
const waitlistClaim = deps.useWaitlistClaim();
|
|
@@ -2074,10 +2450,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2074
2450
|
const currentEvent = selectedEvent ?? preSelectedEvent;
|
|
2075
2451
|
const currentEventPricing = currentEvent ? resolveRuntimeEventPricing(currentEvent) : null;
|
|
2076
2452
|
const currentEventUsesTicketTypes = currentEventPricing !== null && publicEventPricingMode(currentEventPricing) === "ticket_types";
|
|
2077
|
-
const
|
|
2078
|
-
const
|
|
2079
|
-
const currentWaitlistEnabled = selectedModeAvailability?.waitlistEnabled ?? currentEvent?.waitlistEnabled ?? false;
|
|
2080
|
-
const usesTicketTypeSelections = currentEventUsesTicketTypes && !(currentAvailableSpots === 0 && currentWaitlistEnabled === true);
|
|
2453
|
+
const currentCanJoinWaitlist = currentEvent ? canJoinEventWaitlist(currentEvent, { attendanceMode: selectedAttendanceMode }) : false;
|
|
2454
|
+
const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
|
|
2081
2455
|
const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
|
|
2082
2456
|
event: currentEvent,
|
|
2083
2457
|
eligibility: null,
|
|
@@ -2108,7 +2482,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2108
2482
|
});
|
|
2109
2483
|
}, [flowState.ticketsCount, ticketsCount]);
|
|
2110
2484
|
const eligibilityResult = deps.useEligibility({
|
|
2111
|
-
|
|
2485
|
+
refreshKey: optionsRefreshKey,
|
|
2486
|
+
enabled: shouldRefreshAfterRegistrationError(step, errorAction, shouldLoadRegistrationOptions(portalAuthState, step)),
|
|
2112
2487
|
subject: registrationTarget?.subject ?? null,
|
|
2113
2488
|
attendanceMode: selectedAttendanceMode,
|
|
2114
2489
|
ticketSelections,
|
|
@@ -2137,6 +2512,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2137
2512
|
});
|
|
2138
2513
|
}, [waitlistClaim.claim]);
|
|
2139
2514
|
const waitlistRestorationSetters = useMemo(() => ({
|
|
2515
|
+
setErrorAction,
|
|
2140
2516
|
setErrorMessage,
|
|
2141
2517
|
setStep,
|
|
2142
2518
|
setSelectedEvent,
|
|
@@ -2160,6 +2536,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2160
2536
|
if (waitlistClaim.requiresPortalLogin) return;
|
|
2161
2537
|
if (!waitlistClaim.error) return;
|
|
2162
2538
|
setErrorMessage(waitlistClaim.error);
|
|
2539
|
+
setErrorAction("event");
|
|
2163
2540
|
setStep("error");
|
|
2164
2541
|
}, [
|
|
2165
2542
|
waitlistClaim.error,
|
|
@@ -2262,8 +2639,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2262
2639
|
const event = currentEvent;
|
|
2263
2640
|
if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") return false;
|
|
2264
2641
|
if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
|
|
2265
|
-
|
|
2266
|
-
return !((selectedModeAvailability?.availableSpots ?? event.availableSpots) === 0 && (selectedModeAvailability?.waitlistEnabled ?? event.waitlistEnabled) === true);
|
|
2642
|
+
return !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
|
|
2267
2643
|
}, [currentEvent, selectedAttendanceMode]);
|
|
2268
2644
|
const hasValidTicketSelection = !ticketTypeSelectionRequired || normalizedSelectedTicketLines.length > 0 && normalizedSelectedTicketLines.every((line) => {
|
|
2269
2645
|
if (!line.ticketTypeId) return false;
|
|
@@ -2344,6 +2720,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2344
2720
|
]);
|
|
2345
2721
|
const handleDetailsComplete = async () => {
|
|
2346
2722
|
if (ticketTypeSelectionRequired && !hasValidTicketSelection) {
|
|
2723
|
+
setErrorAction("places");
|
|
2347
2724
|
const message = "Choose at least one available ticket to continue.";
|
|
2348
2725
|
setErrorMessage(message);
|
|
2349
2726
|
setStep("error");
|
|
@@ -2354,6 +2731,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2354
2731
|
};
|
|
2355
2732
|
}
|
|
2356
2733
|
if (slidingScaleSelectionMessage) {
|
|
2734
|
+
setErrorAction("places");
|
|
2357
2735
|
setErrorMessage(slidingScaleSelectionMessage);
|
|
2358
2736
|
setStep("error");
|
|
2359
2737
|
return {
|
|
@@ -2371,7 +2749,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2371
2749
|
};
|
|
2372
2750
|
}
|
|
2373
2751
|
if (waitlistClaim.claim === null && eligibility?.isFull && !eligibility.canJoinWaitlist) {
|
|
2374
|
-
const message = "
|
|
2752
|
+
const message = "Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.";
|
|
2753
|
+
setErrorAction("places");
|
|
2375
2754
|
setErrorMessage(message);
|
|
2376
2755
|
setStep("error");
|
|
2377
2756
|
return {
|
|
@@ -2487,6 +2866,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2487
2866
|
const selectionMethod = paymentSelection.method;
|
|
2488
2867
|
const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
|
|
2489
2868
|
const handleSubmit = deps.useEventRegistrationSubmit({
|
|
2869
|
+
setErrorAction,
|
|
2490
2870
|
api,
|
|
2491
2871
|
registrationTarget,
|
|
2492
2872
|
selectedAttendanceMode,
|
|
@@ -2509,26 +2889,31 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2509
2889
|
...current,
|
|
2510
2890
|
consentAccepted: false
|
|
2511
2891
|
}));
|
|
2512
|
-
const
|
|
2513
|
-
setSelectedSlidingAmountInput(
|
|
2514
|
-
setSelectedAttendanceMode(
|
|
2892
|
+
const defaults = getEventSelectionDefaults(event);
|
|
2893
|
+
setSelectedSlidingAmountInput(defaults.slidingAmountInput);
|
|
2894
|
+
setSelectedAttendanceMode(defaults.attendanceMode);
|
|
2515
2895
|
setSelectedTicketLines([]);
|
|
2516
2896
|
dispatchFlow({
|
|
2517
2897
|
type: "event-changed",
|
|
2518
2898
|
hasPreSelectedContext: true
|
|
2519
2899
|
});
|
|
2520
2900
|
};
|
|
2521
|
-
const handleRetry = () => {
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2901
|
+
const handleRetry = () => recoverRegistrationError(resolveRegistrationRecoveryAction(step, errorAction), {
|
|
2902
|
+
canJoinWaitlist: canRecoverToWaitlist(eligibility),
|
|
2903
|
+
refreshOptions: () => setOptionsRefreshKey((value) => value + 1),
|
|
2904
|
+
clearError: () => setErrorMessage(""),
|
|
2905
|
+
setStep,
|
|
2906
|
+
reset: () => resetRegistrationAfterExpiry({
|
|
2907
|
+
hasPreSelectedContext,
|
|
2908
|
+
preSelectedEvent,
|
|
2909
|
+
dispatchFlow,
|
|
2910
|
+
setSelectedTicketLines,
|
|
2911
|
+
setSelectedSlidingAmountInput,
|
|
2912
|
+
setSelectedAttendanceMode,
|
|
2913
|
+
setErrorMessage,
|
|
2914
|
+
setPendingRegistrationReference
|
|
2915
|
+
})
|
|
2916
|
+
});
|
|
2532
2917
|
const handlePaymentStepStateChange = useCallback((state) => {
|
|
2533
2918
|
setPaymentStepState(state);
|
|
2534
2919
|
}, []);
|
|
@@ -2591,6 +2976,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
|
|
|
2591
2976
|
handleGuestShakeEnd,
|
|
2592
2977
|
registrationResult,
|
|
2593
2978
|
errorMessage,
|
|
2979
|
+
errorAction,
|
|
2594
2980
|
altchaSolution,
|
|
2595
2981
|
setAltchaSolution,
|
|
2596
2982
|
portalAuthState,
|
|
@@ -2997,11 +3383,11 @@ function EventRegistrationSummaryBar(props) {
|
|
|
2997
3383
|
*/
|
|
2998
3384
|
function resolveDetailsButtonText(input) {
|
|
2999
3385
|
const { wizard } = input;
|
|
3000
|
-
if (wizard.eligibility?.canJoinWaitlist) return "Join
|
|
3386
|
+
if (wizard.eligibility?.canJoinWaitlist) return "Join waitlist";
|
|
3001
3387
|
if (!input.hasWaitlistClaim && wizard.eligibility?.isFull) return "Sold out";
|
|
3002
3388
|
if (wizard.paymentSelection.method === "pass") return "Book using class pass";
|
|
3003
3389
|
if (wizard.paymentSelection.method === "membership") return input.fallbackText;
|
|
3004
|
-
return wizard.eventRequiresPayment ? "Continue to
|
|
3390
|
+
return wizard.eventRequiresPayment ? "Continue to payment" : input.fallbackText;
|
|
3005
3391
|
}
|
|
3006
3392
|
function resolveExistingRegistrationLabel(wizard) {
|
|
3007
3393
|
return wizard.registrationState.kind === "waitlisted" ? "Already on waitlist" : "Already registered";
|
|
@@ -3013,13 +3399,24 @@ function isExistingRegistrationBlocking$1(wizard) {
|
|
|
3013
3399
|
hasWaitlistClaim: wizard.waitlistClaim.claim !== null
|
|
3014
3400
|
});
|
|
3015
3401
|
}
|
|
3402
|
+
/**
|
|
3403
|
+
* Full for the visitor's selected attendance mode (or the whole event when no
|
|
3404
|
+
* mode is chosen) with no waitlist to fall back on. Reads the same capacity
|
|
3405
|
+
* source as the steps above the action bar so the button never disagrees
|
|
3406
|
+
* with them on a hybrid event.
|
|
3407
|
+
*/
|
|
3408
|
+
function isFullWithNoWaitlist(event, attendanceMode) {
|
|
3409
|
+
if (!event) return false;
|
|
3410
|
+
const source = selectEventCapacitySource(event, attendanceMode);
|
|
3411
|
+
return getEventAvailability(source).isFull && !source.waitlistEnabled;
|
|
3412
|
+
}
|
|
3016
3413
|
function EventRegistrationWizardActions(props) {
|
|
3017
3414
|
const { wizard } = props;
|
|
3018
3415
|
const nextStepAfterTickets = "enter-details";
|
|
3019
3416
|
const prevStepForDetails = "select-tickets";
|
|
3020
3417
|
const hasWaitlistClaim = wizard.waitlistClaim.claim !== null;
|
|
3021
3418
|
const currentEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
|
|
3022
|
-
const isSoldOutWithoutWaitlist = !hasWaitlistClaim && currentEvent
|
|
3419
|
+
const isSoldOutWithoutWaitlist = !hasWaitlistClaim && isFullWithNoWaitlist(currentEvent, wizard.selectedAttendanceMode);
|
|
3023
3420
|
const getDetailsButtonText = () => resolveDetailsButtonText({
|
|
3024
3421
|
wizard,
|
|
3025
3422
|
hasWaitlistClaim,
|
|
@@ -3111,191 +3508,6 @@ function EventRegistrationWizardActions(props) {
|
|
|
3111
3508
|
}
|
|
3112
3509
|
}
|
|
3113
3510
|
//#endregion
|
|
3114
|
-
//#region ../commerce/src/purchase/checkoutPaymentFlow.ts
|
|
3115
|
-
function planCheckoutPaymentFlow(options) {
|
|
3116
|
-
const amountDueCents = toNonNegativeCents(options.amountDueCents);
|
|
3117
|
-
const discountCents = toNonNegativeCents(options.discountCents);
|
|
3118
|
-
const availableCreditCents = toNonNegativeCents(options.availableCreditCents);
|
|
3119
|
-
const minimumStripeChargeCents = toNonNegativeCents(options.minimumStripeChargeCents ?? 0);
|
|
3120
|
-
if (amountDueCents <= 0) return {
|
|
3121
|
-
kind: "bypass",
|
|
3122
|
-
bypassReason: discountCents > 0 ? "discount_free" : "free",
|
|
3123
|
-
reserveCreditCents: 0
|
|
3124
|
-
};
|
|
3125
|
-
if (!options.creditCurrencyMatches || availableCreditCents <= 0) return {
|
|
3126
|
-
kind: "stripe",
|
|
3127
|
-
reserveCreditCents: toNonNegativeCents(0)
|
|
3128
|
-
};
|
|
3129
|
-
if (availableCreditCents >= amountDueCents) return {
|
|
3130
|
-
kind: "store_credit_only",
|
|
3131
|
-
reserveCreditCents: amountDueCents
|
|
3132
|
-
};
|
|
3133
|
-
return {
|
|
3134
|
-
kind: "stripe",
|
|
3135
|
-
reserveCreditCents: planMixedPaymentCreditReservation({
|
|
3136
|
-
amountDueCents,
|
|
3137
|
-
availableCreditCents,
|
|
3138
|
-
minimumStripeChargeCents
|
|
3139
|
-
})
|
|
3140
|
-
};
|
|
3141
|
-
}
|
|
3142
|
-
function planMixedPaymentCreditReservation(input) {
|
|
3143
|
-
const chargeAfterFullCredit = input.amountDueCents - input.availableCreditCents;
|
|
3144
|
-
if (input.minimumStripeChargeCents <= 0 || chargeAfterFullCredit <= 0 || chargeAfterFullCredit >= input.minimumStripeChargeCents) return input.availableCreditCents;
|
|
3145
|
-
if (input.amountDueCents < input.minimumStripeChargeCents) return toNonNegativeCents(0);
|
|
3146
|
-
return toNonNegativeCents(input.amountDueCents - input.minimumStripeChargeCents);
|
|
3147
|
-
}
|
|
3148
|
-
/**
|
|
3149
|
-
* Apply site credit to a flexible-balance booking before card payment.
|
|
3150
|
-
*
|
|
3151
|
-
* Credit first reduces the configured payment due now. Once that payment is
|
|
3152
|
-
* covered, any remaining credit reduces the later balance. If a partial credit
|
|
3153
|
-
* application would leave a positive card charge below Stripe's minimum, less
|
|
3154
|
-
* credit is used so the configured payment can still be collected.
|
|
3155
|
-
*/
|
|
3156
|
-
function planFlexibleBalanceCreditFirst(input) {
|
|
3157
|
-
const totalCents = toNonNegativeCents(input.totalAfterDiscountCents);
|
|
3158
|
-
const configuredDueNowCents = toNonNegativeCents(Math.min(input.configuredPaymentDueNowCents, totalCents));
|
|
3159
|
-
const availableCreditCents = toNonNegativeCents(input.creditCurrencyMatches ? input.availableCreditCents : 0);
|
|
3160
|
-
const minimumStripeChargeCents = toNonNegativeCents(input.minimumStripeChargeCents ?? 0);
|
|
3161
|
-
if (availableCreditCents >= totalCents && totalCents > 0) return {
|
|
3162
|
-
kind: "credit_only",
|
|
3163
|
-
reserveCreditCents: totalCents,
|
|
3164
|
-
cardDueNowCents: toNonNegativeCents(0),
|
|
3165
|
-
laterBalanceCents: toNonNegativeCents(0)
|
|
3166
|
-
};
|
|
3167
|
-
let reserveCreditCents = toNonNegativeCents(Math.min(availableCreditCents, totalCents));
|
|
3168
|
-
if (reserveCreditCents >= configuredDueNowCents) return {
|
|
3169
|
-
kind: "flexible_balance",
|
|
3170
|
-
reserveCreditCents,
|
|
3171
|
-
cardDueNowCents: toNonNegativeCents(0),
|
|
3172
|
-
laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents)
|
|
3173
|
-
};
|
|
3174
|
-
const plannedCardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
|
|
3175
|
-
if (plannedCardDueNowCents > 0 && plannedCardDueNowCents < minimumStripeChargeCents) {
|
|
3176
|
-
if (configuredDueNowCents < minimumStripeChargeCents) return {
|
|
3177
|
-
kind: "card_below_minimum",
|
|
3178
|
-
reserveCreditCents: toNonNegativeCents(0),
|
|
3179
|
-
cardDueNowCents: configuredDueNowCents,
|
|
3180
|
-
laterBalanceCents: toNonNegativeCents(totalCents - configuredDueNowCents)
|
|
3181
|
-
};
|
|
3182
|
-
reserveCreditCents = toNonNegativeCents(configuredDueNowCents - minimumStripeChargeCents);
|
|
3183
|
-
}
|
|
3184
|
-
const cardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
|
|
3185
|
-
return {
|
|
3186
|
-
kind: "flexible_balance",
|
|
3187
|
-
reserveCreditCents,
|
|
3188
|
-
cardDueNowCents,
|
|
3189
|
-
laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents - cardDueNowCents)
|
|
3190
|
-
};
|
|
3191
|
-
}
|
|
3192
|
-
//#endregion
|
|
3193
|
-
//#region ../commerce/src/purchase/settlement.ts
|
|
3194
|
-
function normalizeCurrencyCode(raw) {
|
|
3195
|
-
const normalized = raw.trim().toUpperCase();
|
|
3196
|
-
if (!/^[A-Z]{3}$/.test(normalized)) throw new Error(`Invalid currency code: ${raw}`);
|
|
3197
|
-
return normalized;
|
|
3198
|
-
}
|
|
3199
|
-
//#endregion
|
|
3200
|
-
//#region ../commerce/src/purchase/checkoutSessionMetadata.ts
|
|
3201
|
-
const datedOfferingSharedEngineMetadataShape = {
|
|
3202
|
-
datedOfferingEngineContractVersion: z.literal("shared_v1"),
|
|
3203
|
-
datedOfferingEngineRouteKind: z.literal("shared_whole_series_registration"),
|
|
3204
|
-
datedOfferingCapacityHoldId: z.string().uuid(),
|
|
3205
|
-
datedOfferingCapacityHoldAuditRef: z.string().uuid(),
|
|
3206
|
-
customerId: z.string().uuid(),
|
|
3207
|
-
eventSeriesId: z.string().uuid()
|
|
3208
|
-
};
|
|
3209
|
-
z.object(datedOfferingSharedEngineMetadataShape);
|
|
3210
|
-
const orderCheckoutSessionMetadataBaseShape = {
|
|
3211
|
-
type: z.literal("order"),
|
|
3212
|
-
site_id: z.string().uuid(),
|
|
3213
|
-
order_id: z.string().uuid().optional(),
|
|
3214
|
-
customer_email: z.string().email(),
|
|
3215
|
-
customer_name: z.string().optional(),
|
|
3216
|
-
product_id: z.string().uuid().optional(),
|
|
3217
|
-
attendee_id: z.string().uuid().optional(),
|
|
3218
|
-
occurrence_id: z.string().uuid().optional(),
|
|
3219
|
-
tickets_count: z.string().optional(),
|
|
3220
|
-
notes: z.string().optional(),
|
|
3221
|
-
discount_redemption_id: z.string().uuid().optional(),
|
|
3222
|
-
discount_code_id: z.string().uuid().optional(),
|
|
3223
|
-
discount_code: z.string().optional(),
|
|
3224
|
-
discount_public_label: z.string().optional(),
|
|
3225
|
-
credit_reservation_id: z.string().uuid().optional(),
|
|
3226
|
-
credit_reserved_cents: z.string().optional(),
|
|
3227
|
-
discount_code_amount_cents: z.string().optional(),
|
|
3228
|
-
order_payment_reason: z.enum([
|
|
3229
|
-
"instalment_payoff",
|
|
3230
|
-
"flexible_balance_initial",
|
|
3231
|
-
"flexible_balance_paydown"
|
|
3232
|
-
]).optional(),
|
|
3233
|
-
order_payment_plan_id: z.string().uuid().optional(),
|
|
3234
|
-
order_flexible_balance_contract_id: z.string().uuid().optional()
|
|
3235
|
-
};
|
|
3236
|
-
const absentDatedOfferingSharedEngineMetadataShape = {
|
|
3237
|
-
datedOfferingEngineContractVersion: z.never().optional(),
|
|
3238
|
-
datedOfferingEngineRouteKind: z.never().optional(),
|
|
3239
|
-
datedOfferingCapacityHoldId: z.never().optional(),
|
|
3240
|
-
datedOfferingCapacityHoldAuditRef: z.never().optional(),
|
|
3241
|
-
customerId: z.never().optional(),
|
|
3242
|
-
eventSeriesId: z.never().optional()
|
|
3243
|
-
};
|
|
3244
|
-
const LegacyOrderCheckoutSessionMetadataSchema = z.object({
|
|
3245
|
-
...orderCheckoutSessionMetadataBaseShape,
|
|
3246
|
-
...absentDatedOfferingSharedEngineMetadataShape
|
|
3247
|
-
});
|
|
3248
|
-
const SharedDatedOfferingOrderCheckoutSessionMetadataSchema = z.object({
|
|
3249
|
-
...orderCheckoutSessionMetadataBaseShape,
|
|
3250
|
-
...datedOfferingSharedEngineMetadataShape
|
|
3251
|
-
});
|
|
3252
|
-
z.union([SharedDatedOfferingOrderCheckoutSessionMetadataSchema, LegacyOrderCheckoutSessionMetadataSchema]);
|
|
3253
|
-
toNonNegativeCents(0);
|
|
3254
|
-
//#endregion
|
|
3255
|
-
//#region ../commerce/src/purchase/stripeMinimumCharge.ts
|
|
3256
|
-
const STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY = {
|
|
3257
|
-
GBP: 30,
|
|
3258
|
-
USD: 50,
|
|
3259
|
-
EUR: 50,
|
|
3260
|
-
AED: 200,
|
|
3261
|
-
AUD: 50,
|
|
3262
|
-
BGN: 100,
|
|
3263
|
-
BRL: 50,
|
|
3264
|
-
CAD: 50,
|
|
3265
|
-
CHF: 50,
|
|
3266
|
-
CZK: 1500,
|
|
3267
|
-
DKK: 250,
|
|
3268
|
-
HKD: 400,
|
|
3269
|
-
HUF: 17500,
|
|
3270
|
-
JPY: 50,
|
|
3271
|
-
MXN: 1e3,
|
|
3272
|
-
MYR: 200,
|
|
3273
|
-
NOK: 300,
|
|
3274
|
-
NZD: 50,
|
|
3275
|
-
PLN: 200,
|
|
3276
|
-
RON: 200,
|
|
3277
|
-
SEK: 300,
|
|
3278
|
-
SGD: 50,
|
|
3279
|
-
THB: 1e3
|
|
3280
|
-
};
|
|
3281
|
-
function isStripeMinimumChargeCurrency(currency) {
|
|
3282
|
-
return currency in STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY;
|
|
3283
|
-
}
|
|
3284
|
-
function getStripeMinimumChargeRequirement(rawCurrency) {
|
|
3285
|
-
let currency;
|
|
3286
|
-
try {
|
|
3287
|
-
currency = normalizeCurrencyCode(rawCurrency);
|
|
3288
|
-
} catch {
|
|
3289
|
-
return null;
|
|
3290
|
-
}
|
|
3291
|
-
const currencyKey = currency;
|
|
3292
|
-
if (!isStripeMinimumChargeCurrency(currencyKey)) return null;
|
|
3293
|
-
return {
|
|
3294
|
-
currency,
|
|
3295
|
-
minimumAmountCents: STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY[currencyKey]
|
|
3296
|
-
};
|
|
3297
|
-
}
|
|
3298
|
-
//#endregion
|
|
3299
3511
|
//#region ../blocks/src/system/runtime/nodes/event-registration/siteCreditPreview.ts
|
|
3300
3512
|
function calculateEventRegistrationPreviewTotal(input) {
|
|
3301
3513
|
const discountCents = input.manualDiscountCode.trim() ? 0 : input.automaticDiscount?.computedAmountCents ?? 0;
|
|
@@ -3409,9 +3621,16 @@ function TicketCounter(props) {
|
|
|
3409
3621
|
});
|
|
3410
3622
|
}
|
|
3411
3623
|
function TicketCounterLimit(props) {
|
|
3624
|
+
const showBookingLimit = !props.availabilityLabel || props.maxAllowed < (props.availableSpots ?? Infinity);
|
|
3412
3625
|
return /* @__PURE__ */ jsxs("p", {
|
|
3413
3626
|
className: "er-ticket-counter-limit",
|
|
3414
|
-
children: [/* @__PURE__ */ jsx("span", { children: props.
|
|
3627
|
+
children: [props.availabilityLabel ? /* @__PURE__ */ jsx("span", { children: props.availabilityLabel }) : null, showBookingLimit ? /* @__PURE__ */ jsxs(Fragment, { children: [props.availabilityLabel ? /* @__PURE__ */ jsx("br", {}) : null, /* @__PURE__ */ jsxs("span", { children: [
|
|
3628
|
+
"Up to ",
|
|
3629
|
+
props.maxAllowed,
|
|
3630
|
+
" ",
|
|
3631
|
+
props.quantityLabel,
|
|
3632
|
+
" per booking."
|
|
3633
|
+
] })] }) : null]
|
|
3415
3634
|
});
|
|
3416
3635
|
}
|
|
3417
3636
|
function BookingUnitSessionHint(props) {
|
|
@@ -3427,7 +3646,7 @@ function BookingUnitSessionHint(props) {
|
|
|
3427
3646
|
}
|
|
3428
3647
|
function bookingQuantityLabel(unit, waitlist) {
|
|
3429
3648
|
if (!waitlist) return pluralizeBookingUnit(unit, 2);
|
|
3430
|
-
return
|
|
3649
|
+
return "waitlist places";
|
|
3431
3650
|
}
|
|
3432
3651
|
function BookingQuantityPrompt(props) {
|
|
3433
3652
|
return /* @__PURE__ */ jsxs("h3", {
|
|
@@ -3486,6 +3705,7 @@ function SlidingScaleAmountField(props) {
|
|
|
3486
3705
|
}
|
|
3487
3706
|
function TicketTypeQuantityRows(props) {
|
|
3488
3707
|
const totalSelectedQuantity = getTicketSelectionTotal(props.ticketSelections);
|
|
3708
|
+
const effectiveMaxTickets = props.availableSpots == null ? props.maxTickets : Math.min(props.maxTickets, props.availableSpots);
|
|
3489
3709
|
return /* @__PURE__ */ jsxs("div", {
|
|
3490
3710
|
className: "er-ticket-lines",
|
|
3491
3711
|
children: [
|
|
@@ -3497,6 +3717,7 @@ function TicketTypeQuantityRows(props) {
|
|
|
3497
3717
|
const quantity = getTicketLineQuantity(props.ticketSelections, ticketType.id);
|
|
3498
3718
|
const disabled = ticketType.availability.kind === "unavailable";
|
|
3499
3719
|
const availabilityLabel = getTicketTypeAvailabilityLabel(ticketType);
|
|
3720
|
+
const ticketTypeRemainingLabel = formatTicketTypeRemaining(ticketType.availability.remainingCapacity);
|
|
3500
3721
|
const priceLabel = ticketType.priceAmount > 0 ? formatPrice(ticketType.priceAmount, props.currency) : "Free";
|
|
3501
3722
|
const maxAllowed = disabled ? 0 : getTicketTypeLineMax({
|
|
3502
3723
|
currentQuantity: quantity,
|
|
@@ -3528,9 +3749,9 @@ function TicketTypeQuantityRows(props) {
|
|
|
3528
3749
|
availabilityLabel ? /* @__PURE__ */ jsx("p", {
|
|
3529
3750
|
className: "er-ticket-line-detail",
|
|
3530
3751
|
children: availabilityLabel
|
|
3531
|
-
}) :
|
|
3752
|
+
}) : ticketTypeRemainingLabel ? /* @__PURE__ */ jsx("p", {
|
|
3532
3753
|
className: "er-ticket-line-detail",
|
|
3533
|
-
children:
|
|
3754
|
+
children: ticketTypeRemainingLabel
|
|
3534
3755
|
}) : null
|
|
3535
3756
|
]
|
|
3536
3757
|
}), /* @__PURE__ */ jsx(TicketCounter, {
|
|
@@ -3550,8 +3771,10 @@ function TicketTypeQuantityRows(props) {
|
|
|
3550
3771
|
})
|
|
3551
3772
|
}),
|
|
3552
3773
|
/* @__PURE__ */ jsx(TicketCounterLimit, {
|
|
3553
|
-
|
|
3554
|
-
|
|
3774
|
+
maxAllowed: effectiveMaxTickets,
|
|
3775
|
+
quantityLabel: pluralizeBookingUnit(props.bookingUnit, effectiveMaxTickets),
|
|
3776
|
+
availableSpots: props.availableSpots,
|
|
3777
|
+
availabilityLabel: props.availabilityLabel
|
|
3555
3778
|
}),
|
|
3556
3779
|
totalSelectedQuantity === 0 && props.hasAvailableTicketTypes ? /* @__PURE__ */ jsxs("p", {
|
|
3557
3780
|
className: "er-text er-text--muted",
|
|
@@ -3572,9 +3795,13 @@ function TicketTypeQuantityRows(props) {
|
|
|
3572
3795
|
});
|
|
3573
3796
|
}
|
|
3574
3797
|
function getAttendanceModeDetail(attendanceMode) {
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3798
|
+
const remaining = getRemainingPlaces(attendanceMode);
|
|
3799
|
+
switch (remaining.kind) {
|
|
3800
|
+
case "unlimited": return "Unlimited places";
|
|
3801
|
+
case "open": return "Places available";
|
|
3802
|
+
case "low": return formatRemainingPlaces(attendanceMode);
|
|
3803
|
+
case "full": return remaining.waitlist ? "Currently full, waitlist available" : "Currently full";
|
|
3804
|
+
}
|
|
3578
3805
|
}
|
|
3579
3806
|
function AttendanceModeTiles(props) {
|
|
3580
3807
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -3620,11 +3847,11 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
|
|
|
3620
3847
|
const unit = bookingUnit.kind;
|
|
3621
3848
|
const deliveryMode = event?.deliveryMode ?? "in_person";
|
|
3622
3849
|
const attendanceModes = event?.attendanceModes ?? [];
|
|
3623
|
-
const
|
|
3624
|
-
const
|
|
3625
|
-
const
|
|
3626
|
-
const isSoldOut =
|
|
3627
|
-
const canJoinWaitlist =
|
|
3850
|
+
const capacitySource = event ? selectEventCapacitySource(event, selectedAttendanceMode) : null;
|
|
3851
|
+
const availability = capacitySource ? getEventAvailability(capacitySource) : null;
|
|
3852
|
+
const availableSpots = availability?.available;
|
|
3853
|
+
const isSoldOut = availability?.isFull ?? false;
|
|
3854
|
+
const canJoinWaitlist = capacitySource ? canJoinEventWaitlist(capacitySource) : false;
|
|
3628
3855
|
const eventPricing = event ? resolveRuntimeEventPricing(event) : null;
|
|
3629
3856
|
const eventPricingMode = eventPricing ? publicEventPricingMode(eventPricing) : void 0;
|
|
3630
3857
|
const eventPricingCurrency = eventPricing ? publicEventPricingCurrency(eventPricing) : null;
|
|
@@ -3645,7 +3872,7 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
|
|
|
3645
3872
|
selectedTicketTypeRemainingCapacity: implicitTicketType?.availability.remainingCapacity ?? null,
|
|
3646
3873
|
hasSelectedTicketType: implicitTicketType !== null
|
|
3647
3874
|
});
|
|
3648
|
-
const eventAvailabilityLabel = !canJoinWaitlist &&
|
|
3875
|
+
const eventAvailabilityLabel = !canJoinWaitlist && capacitySource ? formatRemainingPlaces(capacitySource) : null;
|
|
3649
3876
|
if (deliveryMode === "hybrid" && !selectedAttendanceMode) return /* @__PURE__ */ jsxs("div", {
|
|
3650
3877
|
className: "rb-reg-stack er-stack",
|
|
3651
3878
|
children: [
|
|
@@ -3668,7 +3895,7 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
|
|
|
3668
3895
|
className: "rb-reg-stack er-stack",
|
|
3669
3896
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
3670
3897
|
className: "er-title",
|
|
3671
|
-
children: "Sold
|
|
3898
|
+
children: "Sold out"
|
|
3672
3899
|
}), /* @__PURE__ */ jsx("p", {
|
|
3673
3900
|
className: "er-text er-text--muted",
|
|
3674
3901
|
children: "Sorry you missed out! This event is fully booked."
|
|
@@ -3699,6 +3926,7 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
|
|
|
3699
3926
|
ticketSelections,
|
|
3700
3927
|
maxTickets,
|
|
3701
3928
|
availableSpots,
|
|
3929
|
+
availabilityLabel: eventAvailabilityLabel,
|
|
3702
3930
|
currency: eventPricingCurrency ?? "usd",
|
|
3703
3931
|
hasAvailableTicketTypes,
|
|
3704
3932
|
onTicketSelectionsChange,
|
|
@@ -3725,14 +3953,16 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
|
|
|
3725
3953
|
label: bookingQuantityLabel(unit, canJoinWaitlist),
|
|
3726
3954
|
onTicketsChange
|
|
3727
3955
|
}), /* @__PURE__ */ jsx(TicketCounterLimit, {
|
|
3728
|
-
|
|
3956
|
+
maxAllowed,
|
|
3957
|
+
quantityLabel: canJoinWaitlist ? bookingQuantityLabel(unit, true) : pluralizeBookingUnit(unit, maxAllowed),
|
|
3958
|
+
availableSpots: canJoinWaitlist ? null : availableSpots,
|
|
3729
3959
|
availabilityLabel: eventAvailabilityLabel
|
|
3730
3960
|
})]
|
|
3731
3961
|
})
|
|
3732
3962
|
] }),
|
|
3733
3963
|
canJoinWaitlist && /* @__PURE__ */ jsx("p", {
|
|
3734
3964
|
className: "er-text er-text--muted",
|
|
3735
|
-
children: "This event is currently full. Continue to join the waitlist if a
|
|
3965
|
+
children: "This event is currently full. Continue to join the waitlist if a place opens up."
|
|
3736
3966
|
})
|
|
3737
3967
|
]
|
|
3738
3968
|
});
|
|
@@ -4214,15 +4444,14 @@ function resolvePaymentStepStatus(input) {
|
|
|
4214
4444
|
function shouldPromptForLogin(status, isAuthenticated, eligibility) {
|
|
4215
4445
|
return Boolean(status === "not-eligible" && !isAuthenticated && eligibility && (eligibility.requiresAuthForPasses || eligibility.requiresAuthForMemberships));
|
|
4216
4446
|
}
|
|
4217
|
-
function MissingEligibilityPanel(
|
|
4218
|
-
const missing = [!props.occurrenceId && "occurrenceId", !props.email && "email"].filter((part) => Boolean(part));
|
|
4447
|
+
function MissingEligibilityPanel() {
|
|
4219
4448
|
return /* @__PURE__ */ jsx("div", {
|
|
4220
4449
|
className: "rb-reg-stack er-stack",
|
|
4221
4450
|
children: /* @__PURE__ */ jsx("div", {
|
|
4222
4451
|
className: "er-center",
|
|
4223
|
-
children: /* @__PURE__ */
|
|
4452
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
4224
4453
|
className: "er-text er-text--error",
|
|
4225
|
-
children:
|
|
4454
|
+
children: "We couldn’t load the booking options for this event. Please refresh this page and try again. If this keeps happening, contact the organiser for help."
|
|
4226
4455
|
})
|
|
4227
4456
|
})
|
|
4228
4457
|
});
|
|
@@ -4312,10 +4541,7 @@ function PaymentBlockingPanel(props) {
|
|
|
4312
4541
|
})
|
|
4313
4542
|
})
|
|
4314
4543
|
});
|
|
4315
|
-
if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {
|
|
4316
|
-
occurrenceId: props.occurrenceId,
|
|
4317
|
-
email: props.email
|
|
4318
|
-
});
|
|
4544
|
+
if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {});
|
|
4319
4545
|
if (props.eligibility.registration.kind !== "none") {
|
|
4320
4546
|
const isBooked = props.eligibility.registration.kind === "booked";
|
|
4321
4547
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -4323,12 +4549,16 @@ function PaymentBlockingPanel(props) {
|
|
|
4323
4549
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
4324
4550
|
className: "er-title",
|
|
4325
4551
|
children: isBooked ? "Already booked" : "Already on the waitlist"
|
|
4326
|
-
}), /* @__PURE__ */
|
|
4552
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
4327
4553
|
className: "er-center",
|
|
4328
|
-
children: /* @__PURE__ */ jsx("p", {
|
|
4554
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
4329
4555
|
className: "er-text er-text--muted",
|
|
4330
|
-
children: isBooked ? "You
|
|
4331
|
-
})
|
|
4556
|
+
children: isBooked ? "You already have a booking for this event. Please check your bookings to see its status. If you need help, contact the organiser." : "You're already on the waitlist for this event."
|
|
4557
|
+
}), /* @__PURE__ */ jsx("a", {
|
|
4558
|
+
href: "/portal",
|
|
4559
|
+
className: "er-retry-btn",
|
|
4560
|
+
children: "View my bookings"
|
|
4561
|
+
})]
|
|
4332
4562
|
})]
|
|
4333
4563
|
});
|
|
4334
4564
|
}
|
|
@@ -4451,7 +4681,7 @@ function resolvePaymentButtonText(method, siteCreditPreview, eligibility, select
|
|
|
4451
4681
|
const currency = eligibility?.currency ?? "GBP";
|
|
4452
4682
|
switch (method) {
|
|
4453
4683
|
case "stripe":
|
|
4454
|
-
if (siteCreditPreview.kind !== "applied") return selectedDirectOption?.ctaLabel ?? "Continue to
|
|
4684
|
+
if (siteCreditPreview.kind !== "applied") return selectedDirectOption?.ctaLabel ?? "Continue to payment";
|
|
4455
4685
|
if (siteCreditPreview.cardDueNowCents > 0) return `Pay ${formatPrice(siteCreditPreview.cardDueNowCents, currency)} by card`;
|
|
4456
4686
|
return `Book with ${formatPrice(siteCreditPreview.appliedCreditCents, currency)} site credit`;
|
|
4457
4687
|
case "pass": return "Book using class pass";
|
|
@@ -4827,7 +5057,19 @@ function StepIndicator(props) {
|
|
|
4827
5057
|
*
|
|
4828
5058
|
* Displays an error message with a retry button when registration fails.
|
|
4829
5059
|
*/
|
|
4830
|
-
const
|
|
5060
|
+
const labels = {
|
|
5061
|
+
none: "",
|
|
5062
|
+
retry: "Try again",
|
|
5063
|
+
places: "Choose places",
|
|
5064
|
+
start: "Start again",
|
|
5065
|
+
details: "Check my details",
|
|
5066
|
+
"payment-options": "View payment options",
|
|
5067
|
+
"payment-status": "Check payment status",
|
|
5068
|
+
portal: "View my bookings",
|
|
5069
|
+
event: "View event"
|
|
5070
|
+
};
|
|
5071
|
+
const ErrorStep = ({ message, onRetry, action = "retry", canJoinWaitlist = false }) => {
|
|
5072
|
+
const offerWaitlist = action === "places" && canJoinWaitlist;
|
|
4831
5073
|
return /* @__PURE__ */ jsx(TerminalPanel, {
|
|
4832
5074
|
tone: "error",
|
|
4833
5075
|
className: "er-center",
|
|
@@ -4836,16 +5078,20 @@ const ErrorStep = ({ message, onRetry }) => {
|
|
|
4836
5078
|
variant: "error",
|
|
4837
5079
|
children: /* @__PURE__ */ jsx(CrossIcon, {})
|
|
4838
5080
|
}),
|
|
4839
|
-
title: "
|
|
5081
|
+
title: "We couldn’t complete your booking",
|
|
4840
5082
|
body: /* @__PURE__ */ jsx("p", {
|
|
4841
5083
|
className: "er-text--muted er-message",
|
|
4842
|
-
children: message
|
|
5084
|
+
children: offerWaitlist ? "Sorry, this event is now full. You can join the waiting list, and we’ll email you if a place becomes available." : message
|
|
4843
5085
|
}),
|
|
4844
|
-
actions: /* @__PURE__ */ jsx("
|
|
5086
|
+
actions: action === "none" ? void 0 : action === "portal" || action === "payment-status" ? /* @__PURE__ */ jsx("a", {
|
|
5087
|
+
href: "/portal",
|
|
5088
|
+
className: "er-retry-btn",
|
|
5089
|
+
children: labels[action]
|
|
5090
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
4845
5091
|
type: "button",
|
|
4846
5092
|
onClick: onRetry,
|
|
4847
5093
|
className: "er-retry-btn",
|
|
4848
|
-
children: "
|
|
5094
|
+
children: offerWaitlist ? "Join the waiting list" : labels[action]
|
|
4849
5095
|
})
|
|
4850
5096
|
});
|
|
4851
5097
|
};
|
|
@@ -4857,7 +5103,7 @@ const ErrorStep = ({ message, onRetry }) => {
|
|
|
4857
5103
|
* Displays when the payment was definitively declined/failed.
|
|
4858
5104
|
* Provides a retry option that returns to payment selection with form state preserved.
|
|
4859
5105
|
*/
|
|
4860
|
-
const PaymentFailedStep = ({
|
|
5106
|
+
const PaymentFailedStep = ({ onRetry }) => {
|
|
4861
5107
|
return /* @__PURE__ */ jsx(TerminalPanel, {
|
|
4862
5108
|
tone: "error",
|
|
4863
5109
|
className: "er-center",
|
|
@@ -4866,16 +5112,16 @@ const PaymentFailedStep = ({ message, onRetry }) => {
|
|
|
4866
5112
|
variant: "error",
|
|
4867
5113
|
children: /* @__PURE__ */ jsx(CrossIcon, {})
|
|
4868
5114
|
}),
|
|
4869
|
-
title: "
|
|
5115
|
+
title: "Your payment didn’t go through",
|
|
4870
5116
|
body: /* @__PURE__ */ jsx("p", {
|
|
4871
5117
|
className: "er-text--muted er-message",
|
|
4872
|
-
children:
|
|
5118
|
+
children: "Please try again or choose another payment option. If this keeps happening, contact the organiser for help."
|
|
4873
5119
|
}),
|
|
4874
5120
|
actions: /* @__PURE__ */ jsx("button", {
|
|
4875
5121
|
type: "button",
|
|
4876
5122
|
onClick: onRetry,
|
|
4877
5123
|
className: "er-retry-btn",
|
|
4878
|
-
children: "
|
|
5124
|
+
children: "View payment options"
|
|
4879
5125
|
})
|
|
4880
5126
|
});
|
|
4881
5127
|
};
|
|
@@ -4970,7 +5216,7 @@ const VerifyingPaymentStep = () => {
|
|
|
4970
5216
|
* - "Check Again" button to restart polling
|
|
4971
5217
|
* - Contact support option for user reassurance
|
|
4972
5218
|
*/
|
|
4973
|
-
const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "
|
|
5219
|
+
const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "We’re checking your payment", message = "Your booking is not confirmed yet. Please don’t make another booking while we check. You can check your payment status again in a moment." }) => {
|
|
4974
5220
|
return /* @__PURE__ */ jsx(TerminalPanel, {
|
|
4975
5221
|
tone: "warning",
|
|
4976
5222
|
className: "er-center",
|
|
@@ -4989,8 +5235,12 @@ const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "Your regist
|
|
|
4989
5235
|
type: "button",
|
|
4990
5236
|
onClick: onCheckAgain,
|
|
4991
5237
|
className: "er-btn er-btn--primary",
|
|
4992
|
-
children: "Check
|
|
4993
|
-
}) :
|
|
5238
|
+
children: "Check payment status"
|
|
5239
|
+
}) : /* @__PURE__ */ jsx("a", {
|
|
5240
|
+
href: "/portal",
|
|
5241
|
+
className: "er-btn er-btn--primary",
|
|
5242
|
+
children: "Check payment status"
|
|
5243
|
+
}),
|
|
4994
5244
|
secondaryBody: supportEmail ? /* @__PURE__ */ jsxs("p", {
|
|
4995
5245
|
className: "er-text--sm er-text--muted er-message",
|
|
4996
5246
|
children: [
|
|
@@ -5086,7 +5336,7 @@ function EventRegistrationTerminalState(props) {
|
|
|
5086
5336
|
onRetry: () => wizard.setStep("select-payment")
|
|
5087
5337
|
});
|
|
5088
5338
|
case "verifying-timeout": return /* @__PURE__ */ jsx(VerifyingTimeoutStep, {
|
|
5089
|
-
onCheckAgain: wizard.verification.status === "timeout" ? wizard.verification.retry :
|
|
5339
|
+
onCheckAgain: wizard.verification.status === "timeout" ? wizard.verification.retry : wizard.verification.retryReturn,
|
|
5090
5340
|
title: wizard.verification.status === "timeout" ? void 0 : "We’re checking your payment",
|
|
5091
5341
|
message: wizard.verification.status === "timeout" ? void 0 : wizard.errorMessage,
|
|
5092
5342
|
supportEmail: props.supportEmail
|
|
@@ -5098,6 +5348,8 @@ function EventRegistrationTerminalState(props) {
|
|
|
5098
5348
|
}) : null;
|
|
5099
5349
|
case "error": return /* @__PURE__ */ jsx(ErrorStep, {
|
|
5100
5350
|
message: wizard.errorMessage,
|
|
5351
|
+
action: wizard.errorAction,
|
|
5352
|
+
canJoinWaitlist: wizard.eligibility?.isFull && wizard.eligibility.canJoinWaitlist,
|
|
5101
5353
|
onRetry: wizard.handleRetry
|
|
5102
5354
|
});
|
|
5103
5355
|
case "select-event":
|
|
@@ -5255,7 +5507,7 @@ const EVENT_PORTAL_AUTH_COPY = {
|
|
|
5255
5507
|
buttonText: "Email me a login link →"
|
|
5256
5508
|
},
|
|
5257
5509
|
"waitlist-claim": {
|
|
5258
|
-
title: "Log in to claim your waitlist
|
|
5510
|
+
title: "Log in to claim your waitlist place",
|
|
5259
5511
|
buttonText: "Email me a login link to continue →"
|
|
5260
5512
|
}
|
|
5261
5513
|
};
|
|
@@ -5265,11 +5517,12 @@ function getActiveTicketTypes(event) {
|
|
|
5265
5517
|
function formatTicketTypePrice(ticketType, currency) {
|
|
5266
5518
|
return formatCurrencyAmount(ticketType.priceAmount, currency, { decimals: "auto" });
|
|
5267
5519
|
}
|
|
5268
|
-
function getAvailabilityLabel(event) {
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5520
|
+
function getAvailabilityLabel(event, attendanceMode) {
|
|
5521
|
+
if (!event) return null;
|
|
5522
|
+
return formatRemainingPlaces(selectEventCapacitySource(event, attendanceMode), {
|
|
5523
|
+
full: "Fully booked",
|
|
5524
|
+
waitlist: "Waitlist available"
|
|
5525
|
+
});
|
|
5273
5526
|
}
|
|
5274
5527
|
function getWizardStepLabel(input) {
|
|
5275
5528
|
switch (input.step) {
|
|
@@ -5366,7 +5619,7 @@ function EventRegistrationWizard(props) {
|
|
|
5366
5619
|
const currentStepNumber = getStepNumber(wizard.step, activeWizardSteps);
|
|
5367
5620
|
const summaryEventPricingType = summaryEvent ? publicEventPricingType(summaryEvent.pricing) : "free";
|
|
5368
5621
|
const summaryPriceLabel = summaryEvent ? formatEventPriceDisplay(summaryEvent.pricing) : null;
|
|
5369
|
-
const summaryAvailabilityLabel = getAvailabilityLabel(summaryEvent);
|
|
5622
|
+
const summaryAvailabilityLabel = getAvailabilityLabel(summaryEvent, wizard.selectedAttendanceMode);
|
|
5370
5623
|
const waitlistClaimRequiresPortalLogin = wizard.waitlistClaim.requiresPortalLogin && wizard.portalAuthState !== "authenticated" && wizard.portalAuthState !== "checking";
|
|
5371
5624
|
const portalAuthCopy = waitlistClaimRequiresPortalLogin ? {
|
|
5372
5625
|
...EVENT_PORTAL_AUTH_COPY["waitlist-claim"],
|